[systemd-commits] Makefile.am src/libsystemd
Tom Gundersen
tomegun at kemper.freedesktop.org
Tue Jan 21 05:41:36 PST 2014
Makefile.am | 154
src/libsystemd/.gitignore | 1
src/libsystemd/DIFFERENCES | 28
src/libsystemd/GVARIANT-SERIALIZATION | 63
src/libsystemd/PORTING-DBUS1 | 550 --
src/libsystemd/bus-bloom.c | 99
src/libsystemd/bus-bloom.h | 29
src/libsystemd/bus-container.c | 241
src/libsystemd/bus-container.h | 27
src/libsystemd/bus-control.c | 1217 ----
src/libsystemd/bus-control.h | 30
src/libsystemd/bus-convenience.c | 442 -
src/libsystemd/bus-creds.c | 904 ---
src/libsystemd/bus-creds.h | 75
src/libsystemd/bus-dump.c | 417 -
src/libsystemd/bus-dump.h | 31
src/libsystemd/bus-error-mapping.gperf | 49
src/libsystemd/bus-error.c | 504 --
src/libsystemd/bus-error.h | 42
src/libsystemd/bus-gvariant.c | 249
src/libsystemd/bus-gvariant.h | 26
src/libsystemd/bus-internal.c | 305 -
src/libsystemd/bus-internal.h | 328 -
src/libsystemd/bus-introspect.c | 212
src/libsystemd/bus-introspect.h | 42
src/libsystemd/bus-kernel.c | 1348 -----
src/libsystemd/bus-kernel.h | 81
src/libsystemd/bus-match.c | 1078 ----
src/libsystemd/bus-match.h | 93
src/libsystemd/bus-message.c | 5535 ----------------------
src/libsystemd/bus-message.h | 244
src/libsystemd/bus-objects.c | 2507 ---------
src/libsystemd/bus-objects.h | 26
src/libsystemd/bus-protocol.h | 147
src/libsystemd/bus-signature.c | 160
src/libsystemd/bus-signature.h | 31
src/libsystemd/bus-socket.c | 1105 ----
src/libsystemd/bus-socket.h | 39
src/libsystemd/bus-type.c | 179
src/libsystemd/bus-type.h | 36
src/libsystemd/bus-util.c | 1232 ----
src/libsystemd/bus-util.h | 180
src/libsystemd/busctl.c | 514 --
src/libsystemd/event-util.h | 30
src/libsystemd/kdbus.h | 900 ---
src/libsystemd/resolve-util.h | 29
src/libsystemd/rtnl-internal.h | 99
src/libsystemd/rtnl-message.c | 887 ---
src/libsystemd/rtnl-util.c | 100
src/libsystemd/rtnl-util.h | 36
src/libsystemd/sd-bus.c | 3032 ------------
src/libsystemd/sd-bus/.gitignore | 1
src/libsystemd/sd-bus/DIFFERENCES | 28
src/libsystemd/sd-bus/GVARIANT-SERIALIZATION | 63
src/libsystemd/sd-bus/Makefile | 1
src/libsystemd/sd-bus/PORTING-DBUS1 | 550 ++
src/libsystemd/sd-bus/bus-bloom.c | 99
src/libsystemd/sd-bus/bus-bloom.h | 29
src/libsystemd/sd-bus/bus-container.c | 241
src/libsystemd/sd-bus/bus-container.h | 27
src/libsystemd/sd-bus/bus-control.c | 1217 ++++
src/libsystemd/sd-bus/bus-control.h | 30
src/libsystemd/sd-bus/bus-convenience.c | 442 +
src/libsystemd/sd-bus/bus-creds.c | 904 +++
src/libsystemd/sd-bus/bus-creds.h | 75
src/libsystemd/sd-bus/bus-dump.c | 417 +
src/libsystemd/sd-bus/bus-dump.h | 31
src/libsystemd/sd-bus/bus-error-mapping.gperf | 49
src/libsystemd/sd-bus/bus-error.c | 504 ++
src/libsystemd/sd-bus/bus-error.h | 42
src/libsystemd/sd-bus/bus-gvariant.c | 249
src/libsystemd/sd-bus/bus-gvariant.h | 26
src/libsystemd/sd-bus/bus-internal.c | 305 +
src/libsystemd/sd-bus/bus-internal.h | 328 +
src/libsystemd/sd-bus/bus-introspect.c | 212
src/libsystemd/sd-bus/bus-introspect.h | 42
src/libsystemd/sd-bus/bus-kernel.c | 1348 +++++
src/libsystemd/sd-bus/bus-kernel.h | 81
src/libsystemd/sd-bus/bus-match.c | 1078 ++++
src/libsystemd/sd-bus/bus-match.h | 93
src/libsystemd/sd-bus/bus-message.c | 5535 ++++++++++++++++++++++
src/libsystemd/sd-bus/bus-message.h | 244
src/libsystemd/sd-bus/bus-objects.c | 2507 +++++++++
src/libsystemd/sd-bus/bus-objects.h | 26
src/libsystemd/sd-bus/bus-protocol.h | 147
src/libsystemd/sd-bus/bus-signature.c | 160
src/libsystemd/sd-bus/bus-signature.h | 31
src/libsystemd/sd-bus/bus-socket.c | 1105 ++++
src/libsystemd/sd-bus/bus-socket.h | 39
src/libsystemd/sd-bus/bus-type.c | 179
src/libsystemd/sd-bus/bus-type.h | 36
src/libsystemd/sd-bus/bus-util.c | 1232 ++++
src/libsystemd/sd-bus/bus-util.h | 180
src/libsystemd/sd-bus/busctl.c | 514 ++
src/libsystemd/sd-bus/kdbus.h | 900 +++
src/libsystemd/sd-bus/sd-bus.c | 3032 ++++++++++++
src/libsystemd/sd-bus/sd-memfd.c | 220
src/libsystemd/sd-bus/test-bus-chat.c | 581 ++
src/libsystemd/sd-bus/test-bus-cleanup.c | 80
src/libsystemd/sd-bus/test-bus-creds.c | 46
src/libsystemd/sd-bus/test-bus-error.c | 115
src/libsystemd/sd-bus/test-bus-gvariant.c | 201
src/libsystemd/sd-bus/test-bus-introspect.c | 66
src/libsystemd/sd-bus/test-bus-kernel-benchmark.c | 303 +
src/libsystemd/sd-bus/test-bus-kernel-bloom.c | 115
src/libsystemd/sd-bus/test-bus-kernel.c | 175
src/libsystemd/sd-bus/test-bus-marshal.c | 324 +
src/libsystemd/sd-bus/test-bus-match.c | 145
src/libsystemd/sd-bus/test-bus-memfd.c | 174
src/libsystemd/sd-bus/test-bus-objects.c | 500 +
src/libsystemd/sd-bus/test-bus-server.c | 224
src/libsystemd/sd-bus/test-bus-signature.c | 163
src/libsystemd/sd-bus/test-bus-zero-copy.c | 197
src/libsystemd/sd-event.c | 2224 --------
src/libsystemd/sd-event/Makefile | 1
src/libsystemd/sd-event/event-util.h | 30
src/libsystemd/sd-event/sd-event.c | 2224 ++++++++
src/libsystemd/sd-event/test-event.c | 248
src/libsystemd/sd-memfd.c | 220
src/libsystemd/sd-resolve.c | 1130 ----
src/libsystemd/sd-resolve/Makefile | 1
src/libsystemd/sd-resolve/resolve-util.h | 29
src/libsystemd/sd-resolve/sd-resolve.c | 1130 ++++
src/libsystemd/sd-resolve/test-resolve.c | 160
src/libsystemd/sd-rtnl.c | 870 ---
src/libsystemd/sd-rtnl/Makefile | 1
src/libsystemd/sd-rtnl/rtnl-internal.h | 99
src/libsystemd/sd-rtnl/rtnl-message.c | 887 +++
src/libsystemd/sd-rtnl/rtnl-util.c | 100
src/libsystemd/sd-rtnl/rtnl-util.h | 36
src/libsystemd/sd-rtnl/sd-rtnl.c | 870 +++
src/libsystemd/sd-rtnl/test-rtnl.c | 330 +
src/libsystemd/sd-utf8.c | 36
src/libsystemd/sd-utf8/Makefile | 1
src/libsystemd/sd-utf8/sd-utf8.c | 36
src/libsystemd/test-bus-chat.c | 581 --
src/libsystemd/test-bus-cleanup.c | 80
src/libsystemd/test-bus-creds.c | 46
src/libsystemd/test-bus-error.c | 115
src/libsystemd/test-bus-gvariant.c | 201
src/libsystemd/test-bus-introspect.c | 66
src/libsystemd/test-bus-kernel-benchmark.c | 303 -
src/libsystemd/test-bus-kernel-bloom.c | 115
src/libsystemd/test-bus-kernel.c | 175
src/libsystemd/test-bus-marshal.c | 324 -
src/libsystemd/test-bus-match.c | 145
src/libsystemd/test-bus-memfd.c | 174
src/libsystemd/test-bus-objects.c | 500 -
src/libsystemd/test-bus-server.c | 224
src/libsystemd/test-bus-signature.c | 163
src/libsystemd/test-bus-zero-copy.c | 197
src/libsystemd/test-event.c | 248
src/libsystemd/test-resolve.c | 160
src/libsystemd/test-rtnl.c | 330 -
154 files changed, 34269 insertions(+), 34262 deletions(-)
New commits:
commit 607553f9306286fdccf0b356bc3d1087adfe21c4
Author: Tom Gundersen <teg at jklm.no>
Date: Tue Jan 21 14:10:18 2014 +0100
libsystemd: split up into subdirs
We still only produce on .so, but let's keep the sources separate to make things a bit
less messy.
diff --git a/Makefile.am b/Makefile.am
index 8a897cd..935a195 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -189,7 +189,9 @@ AM_CPPFLAGS = \
-I $(top_srcdir)/src/udev \
-I $(top_srcdir)/src/udev/net \
-I $(top_builddir)/src/udev \
- -I $(top_srcdir)/src/libsystemd \
+ -I $(top_srcdir)/src/libsystemd/sd-bus \
+ -I $(top_srcdir)/src/libsystemd/sd-event \
+ -I $(top_srcdir)/src/libsystemd/sd-rtnl \
$(OUR_CPPFLAGS)
AM_CFLAGS = $(OUR_CFLAGS)
@@ -1984,56 +1986,56 @@ libsystemd_la_SOURCES = \
src/systemd/sd-event.h \
src/systemd/sd-rtnl.h \
src/systemd/sd-resolve.h \
- src/libsystemd/sd-bus.c \
- src/libsystemd/bus-control.c \
- src/libsystemd/bus-control.h \
- src/libsystemd/bus-error.c \
- src/libsystemd/bus-error.h \
- src/libsystemd/bus-internal.c \
- src/libsystemd/bus-internal.h \
- src/libsystemd/bus-socket.c \
- src/libsystemd/bus-socket.h \
- src/libsystemd/bus-kernel.c \
- src/libsystemd/bus-kernel.h \
- src/libsystemd/bus-container.c \
- src/libsystemd/bus-container.h \
- src/libsystemd/bus-message.c \
- src/libsystemd/bus-message.h \
- src/libsystemd/bus-creds.c \
- src/libsystemd/bus-creds.h \
- src/libsystemd/bus-signature.c \
- src/libsystemd/bus-signature.h \
- src/libsystemd/bus-type.c \
- src/libsystemd/bus-type.h \
- src/libsystemd/bus-match.c \
- src/libsystemd/bus-match.h \
- src/libsystemd/bus-bloom.c \
- src/libsystemd/bus-bloom.h \
- src/libsystemd/bus-introspect.c \
- src/libsystemd/bus-introspect.h \
- src/libsystemd/bus-objects.c \
- src/libsystemd/bus-objects.h \
- src/libsystemd/bus-gvariant.c \
- src/libsystemd/bus-gvariant.h \
- src/libsystemd/bus-convenience.c \
- src/libsystemd/kdbus.h \
- src/libsystemd/sd-memfd.c \
- src/libsystemd/bus-util.c \
- src/libsystemd/bus-util.h \
- src/libsystemd/sd-utf8.c \
- src/libsystemd/sd-event.c \
- src/libsystemd/event-util.h \
- src/libsystemd/bus-protocol.h \
- src/libsystemd/sd-rtnl.c \
- src/libsystemd/rtnl-internal.h \
- src/libsystemd/rtnl-message.c \
- src/libsystemd/rtnl-util.h \
- src/libsystemd/rtnl-util.c \
- src/libsystemd/sd-resolve.c \
- src/libsystemd/resolve-util.h
+ src/libsystemd/sd-bus/sd-bus.c \
+ src/libsystemd/sd-bus/bus-control.c \
+ src/libsystemd/sd-bus/bus-control.h \
+ src/libsystemd/sd-bus/bus-error.c \
+ src/libsystemd/sd-bus/bus-error.h \
+ src/libsystemd/sd-bus/bus-internal.c \
+ src/libsystemd/sd-bus/bus-internal.h \
+ src/libsystemd/sd-bus/bus-socket.c \
+ src/libsystemd/sd-bus/bus-socket.h \
+ src/libsystemd/sd-bus/bus-kernel.c \
+ src/libsystemd/sd-bus/bus-kernel.h \
+ src/libsystemd/sd-bus/bus-container.c \
+ src/libsystemd/sd-bus/bus-container.h \
+ src/libsystemd/sd-bus/bus-message.c \
+ src/libsystemd/sd-bus/bus-message.h \
+ src/libsystemd/sd-bus/bus-creds.c \
+ src/libsystemd/sd-bus/bus-creds.h \
+ src/libsystemd/sd-bus/bus-signature.c \
+ src/libsystemd/sd-bus/bus-signature.h \
+ src/libsystemd/sd-bus/bus-type.c \
+ src/libsystemd/sd-bus/bus-type.h \
+ src/libsystemd/sd-bus/bus-match.c \
+ src/libsystemd/sd-bus/bus-match.h \
+ src/libsystemd/sd-bus/bus-bloom.c \
+ src/libsystemd/sd-bus/bus-bloom.h \
+ src/libsystemd/sd-bus/bus-introspect.c \
+ src/libsystemd/sd-bus/bus-introspect.h \
+ src/libsystemd/sd-bus/bus-objects.c \
+ src/libsystemd/sd-bus/bus-objects.h \
+ src/libsystemd/sd-bus/bus-gvariant.c \
+ src/libsystemd/sd-bus/bus-gvariant.h \
+ src/libsystemd/sd-bus/bus-convenience.c \
+ src/libsystemd/sd-bus/bus-util.c \
+ src/libsystemd/sd-bus/bus-util.h \
+ src/libsystemd/sd-bus/bus-protocol.h \
+ src/libsystemd/sd-bus/kdbus.h \
+ src/libsystemd/sd-bus/sd-memfd.c \
+ src/libsystemd/sd-utf8/sd-utf8.c \
+ src/libsystemd/sd-event/sd-event.c \
+ src/libsystemd/sd-event/event-util.h \
+ src/libsystemd/sd-rtnl/sd-rtnl.c \
+ src/libsystemd/sd-rtnl/rtnl-internal.h \
+ src/libsystemd/sd-rtnl/rtnl-message.c \
+ src/libsystemd/sd-rtnl/rtnl-util.h \
+ src/libsystemd/sd-rtnl/rtnl-util.c \
+ src/libsystemd/sd-resolve/sd-resolve.c \
+ src/libsystemd/sd-resolve/resolve-util.h
nodist_libsystemd_la_SOURCES = \
- src/libsystemd/bus-error-mapping.c
+ src/libsystemd/sd-bus/bus-error-mapping.c
libsystemd_la_LIBADD = \
libsystemd-id128-internal.la \
@@ -2054,13 +2056,13 @@ pkgconfiglib_DATA += \
src/libsystemd/libsystemd.pc
EXTRA_DIST += \
- src/libsystemd/bus-error-mapping.gperf \
- src/libsystemd/libsystemd.pc.in \
- src/libsystemd/DIFFERENCES \
- src/libsystemd/GVARIANT-SERIALIZATION
+ src/libsystemd/sd-bus/bus-error-mapping.gperf \
+ src/libsystemd/sd-bus/DIFFERENCES \
+ src/libsystemd/sd-bus/GVARIANT-SERIALIZATION \
+ src/libsystemd/libsystemd.pc.in
CLEANFILES += \
- src/libsystemd/bus-error-mapping.c
+ src/libsystemd/sd-bus/bus-error-mapping.c
lib_LTLIBRARIES += \
libsystemd.la
@@ -2078,8 +2080,8 @@ noinst_LTLIBRARIES += \
libsystemd-internal.la
libsystemd_dump_la_SOURCES = \
- src/libsystemd/bus-dump.c \
- src/libsystemd/bus-dump.h
+ src/libsystemd/sd-bus/bus-dump.c \
+ src/libsystemd/sd-bus/bus-dump.h
libsystemd_dump_la_CFLAGS = \
$(AM_CFLAGS) \
@@ -2113,7 +2115,7 @@ bin_PROGRAMS += \
busctl
test_bus_marshal_SOURCES = \
- src/libsystemd/test-bus-marshal.c
+ src/libsystemd/sd-bus/test-bus-marshal.c
test_bus_marshal_LDADD = \
libsystemd-internal.la \
@@ -2133,14 +2135,14 @@ test_bus_marshal_CFLAGS = \
$(CAP_CFLAGS)
test_bus_signature_SOURCES = \
- src/libsystemd/test-bus-signature.c
+ src/libsystemd/sd-bus/test-bus-signature.c
test_bus_signature_LDADD = \
libsystemd-shared.la \
libsystemd-internal.la
test_bus_chat_SOURCES = \
- src/libsystemd/test-bus-chat.c
+ src/libsystemd/sd-bus/test-bus-chat.c
test_bus_chat_CFLAGS = \
$(AM_CFLAGS) \
@@ -2153,7 +2155,7 @@ test_bus_chat_LDADD = \
libsystemd-shared.la
test_bus_cleanup_SOURCES = \
- src/libsystemd/test-bus-cleanup.c
+ src/libsystemd/sd-bus/test-bus-cleanup.c
test_bus_cleanup_LDADD = \
libsystemd-internal.la \
@@ -2162,7 +2164,7 @@ test_bus_cleanup_LDADD = \
libsystemd-shared.la
test_bus_server_SOURCES = \
- src/libsystemd/test-bus-server.c
+ src/libsystemd/sd-bus/test-bus-server.c
test_bus_server_CFLAGS = \
$(AM_CFLAGS) \
@@ -2175,7 +2177,7 @@ test_bus_server_LDADD = \
libsystemd-shared.la
test_bus_objects_SOURCES = \
- src/libsystemd/test-bus-objects.c
+ src/libsystemd/sd-bus/test-bus-objects.c
test_bus_objects_CFLAGS = \
$(AM_CFLAGS) \
@@ -2192,7 +2194,7 @@ test_bus_objects_LDADD = \
$(CAP_LIBS)
test_bus_error_SOURCES = \
- src/libsystemd/test-bus-error.c
+ src/libsystemd/sd-bus/test-bus-error.c
test_bus_error_LDADD = \
libsystemd-internal.la \
@@ -2201,7 +2203,7 @@ test_bus_error_LDADD = \
libsystemd-shared.la
test_bus_gvariant_SOURCES = \
- src/libsystemd/test-bus-gvariant.c
+ src/libsystemd/sd-bus/test-bus-gvariant.c
test_bus_gvariant_LDADD = \
libsystemd-internal.la \
@@ -2219,7 +2221,7 @@ test_bus_gvariant_CFLAGS = \
$(CAP_CFLAGS)
test_bus_creds_SOURCES = \
- src/libsystemd/test-bus-creds.c
+ src/libsystemd/sd-bus/test-bus-creds.c
test_bus_creds_LDADD = \
libsystemd-internal.la \
@@ -2230,7 +2232,7 @@ test_bus_creds_LDADD = \
libsystemd-capability.la
test_bus_match_SOURCES = \
- src/libsystemd/test-bus-match.c
+ src/libsystemd/sd-bus/test-bus-match.c
test_bus_match_LDADD = \
libsystemd-internal.la \
@@ -2239,7 +2241,7 @@ test_bus_match_LDADD = \
libsystemd-shared.la
test_bus_kernel_SOURCES = \
- src/libsystemd/test-bus-kernel.c
+ src/libsystemd/sd-bus/test-bus-kernel.c
test_bus_kernel_LDADD = \
libsystemd-internal.la \
@@ -2255,7 +2257,7 @@ test_bus_kernel_CFLAGS = \
$(CAP_CFLAGS)
test_bus_kernel_bloom_SOURCES = \
- src/libsystemd/test-bus-kernel-bloom.c
+ src/libsystemd/sd-bus/test-bus-kernel-bloom.c
test_bus_kernel_bloom_LDADD = \
libsystemd-internal.la \
@@ -2264,7 +2266,7 @@ test_bus_kernel_bloom_LDADD = \
libsystemd-shared.la
test_bus_kernel_benchmark_SOURCES = \
- src/libsystemd/test-bus-kernel-benchmark.c
+ src/libsystemd/sd-bus/test-bus-kernel-benchmark.c
test_bus_kernel_benchmark_LDADD = \
libsystemd-internal.la \
@@ -2273,14 +2275,14 @@ test_bus_kernel_benchmark_LDADD = \
libsystemd-shared.la
test_bus_memfd_SOURCES = \
- src/libsystemd/test-bus-memfd.c
+ src/libsystemd/sd-bus/test-bus-memfd.c
test_bus_memfd_LDADD = \
libsystemd-internal.la \
libsystemd-shared.la
test_bus_zero_copy_SOURCES = \
- src/libsystemd/test-bus-zero-copy.c
+ src/libsystemd/sd-bus/test-bus-zero-copy.c
test_bus_zero_copy_LDADD = \
libsystemd-internal.la \
@@ -2296,14 +2298,14 @@ test_bus_zero_copy_CFLAGS = \
$(CAP_CFLAGS)
test_bus_introspect_SOURCES = \
- src/libsystemd/test-bus-introspect.c
+ src/libsystemd/sd-bus/test-bus-introspect.c
test_bus_introspect_LDADD = \
libsystemd-shared.la \
libsystemd-internal.la
test_event_SOURCES = \
- src/libsystemd/test-event.c
+ src/libsystemd/sd-event/test-event.c
test_event_LDADD = \
libsystemd-internal.la \
@@ -2312,7 +2314,7 @@ test_event_LDADD = \
libsystemd-shared.la
test_rtnl_SOURCES = \
- src/libsystemd/test-rtnl.c
+ src/libsystemd/sd-rtnl/test-rtnl.c
test_rtnl_LDADD = \
libsystemd-internal.la \
@@ -2322,7 +2324,7 @@ test_rtnl_LDADD = \
test_resolve_SOURCES = \
src/systemd/sd-resolve.h \
- src/libsystemd/test-resolve.c
+ src/libsystemd/sd-resolve/test-resolve.c
test_resolve_LDADD = \
libsystemd-internal.la \
@@ -2333,7 +2335,7 @@ test_resolve_CFLAGS = \
-pthread
busctl_SOURCES = \
- src/libsystemd/busctl.c
+ src/libsystemd/sd-bus/busctl.c
busctl_LDADD = \
libsystemd-internal.la \
diff --git a/src/libsystemd/.gitignore b/src/libsystemd/.gitignore
index 613b05c..50a1692 100644
--- a/src/libsystemd/.gitignore
+++ b/src/libsystemd/.gitignore
@@ -1,2 +1 @@
/libsystemd.pc
-/bus-error-mapping.c
diff --git a/src/libsystemd/DIFFERENCES b/src/libsystemd/DIFFERENCES
deleted file mode 100644
index fd7506b..0000000
--- a/src/libsystemd/DIFFERENCES
+++ /dev/null
@@ -1,28 +0,0 @@
-Known differences between dbus1 and kdbus:
-
-- NameAcquired/NameLost is gone entirely on kdbus backends if
- libsystemd is used. It is still added in by systemd-bus-proxyd
- for old dbus1 clients, and it is available if libsystemd is used
- against the classic dbus1 daemon. If you want to write compatible
- code with libsystem-bus you need to explicitly subscribe to
- NameOwnerChanged signals and just ignore NameAcquired/NameLost
-
-- Applications have to deal with spurious signals they didn't expect,
- due to the probabilistic bloom filters. They need to handle this
- anyway, given that any client can send anything to arbitrary clients
- anyway, even in dbus1, so not much changes.
-
-- clients of the system bus when kdbus is used must roll their own
- security. Only legacy dbus1 clients get the old XML policy enforced,
- which is implemented by systemd-bus-proxyd.
-
-- Serial numbers of synthesized messages are always (uint32_t) -1.
-
-- The org.freedesktop.DBus "driver" service is not special on
- kdbus. It is a bus activated service like any other with its own
- unique name.
-
-- NameOwnerChanged is a synthetic message, generated locally and not
- by the driver.
-
-- There's no standard per-session bus anymore. Only a per-user bus.
diff --git a/src/libsystemd/GVARIANT-SERIALIZATION b/src/libsystemd/GVARIANT-SERIALIZATION
deleted file mode 100644
index 5dffc25..0000000
--- a/src/libsystemd/GVARIANT-SERIALIZATION
+++ /dev/null
@@ -1,63 +0,0 @@
-How we use GVariant for serializing D-Bus messages
---------------------------------------------------
-
-We stay as close to the original dbus1 framing as possible. dbus1 has
-the following framing:
-
- 1. A fixed header of "yyyyuu"
- 2. Additional header fields of "a(yv)"
- 3. Padding with NUL bytes to pad up to next 8byte boundary
- 4. The body
-
-Note that the body is not padded at the end, the complete message
-hence might have a non-aligned size. Reading multiple messages at once
-will hence result in possibly unaligned messages in memory.
-
-The header consists of the following:
-
- y Endianness, 'l' or 'B'
- y Message Type
- y Flags
- y Protocol version, '1'
- u Length of the body, i.e. the length of part 4 above
- u Serial number
-
- = 12 bytes
-
-When using GVariant we keep the basic structure in place, only
-slightly extend the header, and define protocol version '2'. The new
-header:
-
- y Endianness, 'l' or 'B'
- y Message Type
- y Flags
- y Protocol version, '2'
- u Length of the body, i.e. the length of part 4 above
- u Serial number
- u Length of the additional header fields array
-
- = 16 bytes
-
-This has the nice benefit that the beginning of the additional header
-fields array is aligned to an 8 byte boundary. Also, in dbus1
-marshalling arrays start with a length value of 32bit, which means in
-both dbus1 and gvariant marshallings the size of the header fields
-array will be at the same location between bytes 12 and 16. To
-visualize that:
-
- 0 4 8 12 16
- Common: | E | T | F | V | Body Length | Serial | Fields Length |
-
- dbus1: | ... (as above) ... | Fields array ...
-
- gvariant: | ... (as above) ... | Fields Length | Fields array ...
-
-And that's already it.
-
-Note: on kdbus only native endian messages marshalled in gvariant may
- be sent. If a client receives a message in non-native endianness
- or in dbus1 marshalling it shall ignore the message.
-
-Note: The GVariant "MAYBE" type is not supported, so that messages can
- be fully converted forth and back between dbus1 and gvariant
- representations.
diff --git a/src/libsystemd/PORTING-DBUS1 b/src/libsystemd/PORTING-DBUS1
deleted file mode 100644
index 67af277..0000000
--- a/src/libsystemd/PORTING-DBUS1
+++ /dev/null
@@ -1,550 +0,0 @@
-A few hints on supporting kdbus as backend in your favorite D-Bus library.
-
-~~~
-
-Before you read this, have a look at the DIFFERENCES and
-GVARIANT_SERIALIZATION texts you find in the same directory where you
-found this.
-
-We invite you to port your favorite D-Bus protocol implementation
-over to kdbus. However, there are a couple of complexities
-involved. On kdbus we only speak GVariant marshaling, kdbus clients
-ignore traffic in dbus1 marshaling. Thus, you need to add a second,
-GVariant compatible marshaler to your library first.
-
-After you have done that: here's the basic principle how kdbus works:
-
-You connect to a bus by opening its bus node in /dev/kdbus/. All
-buses have a device node there, it starts with a numeric UID of the
-owner of the bus, followed by a dash and a string identifying the
-bus. The system bus is thus called /dev/kdbus/0-system, and for user
-buses the device node is /dev/kdbus/1000-user (if 1000 is your user
-id).
-
-(Before we proceed, please always keep a copy of libsystemd next
-to you, ultimately that's where the details are, this document simply
-is a rough overview to help you grok things.)
-
-CONNECTING
-
-To connect to a bus, simply open() its device node and issue the
-KDBUS_CMD_HELLO call. That's it. Now you are connected. Do not send
-Hello messages or so (as you would on dbus1), that does not exist for
-kdbus.
-
-The structure you pass to the ioctl will contain a couple of
-parameters that you need to know, to operate on the bus.
-
-There are two flags fields, one indicating features of the kdbus
-kernel side ("conn_flags"), the other one ("bus_flags") indicating
-features of the bus owner (i.e. systemd). Both flags fields are 64bit
-in width.
-
-When calling into the ioctl, you need to place your own supported
-feature bits into these fields. This tells the kernel about the
-features you support. When the ioctl returns, it will contain the
-features the kernel supports.
-
-If any of the higher 32bit are set on the two flags fields and your
-client does not know what they mean, it must disconnect. The upper
-32bit are used to indicate "incompatible" feature additions on the bus
-system, the lower 32bit indicate "compatible" feature additions. A
-client that does not support a "compatible" feature addition can go on
-communicating with the bus, however a client that does not support an
-"incompatible" feature must not proceed with the connection.
-
-The hello structure also contains another flags field "attach_flags"
-which indicates metadata that is optionally attached to all incoming
-messages. You probably want to set KDBUS_ATTACH_NAMES unconditionally
-in it. This has the effect that all well-known names of a sender are
-attached to all incoming messages. You need this information to
-implement matches that match on a message sender name correctly. Of
-course, you should only request the attachment of as little metadata
-fields as you need.
-
-The kernel will return in the "id" field your unique id. This is a
-simple numeric value. For compatibility with classic dbus1 simply
-format this as string and prefix ":0.".
-
-The kernel will also return the bloom filter size used for the signal
-broadcast bloom filter (see below).
-
-The kernel will also return the bus ID of the bus in a 128bit field.
-
-The pool size field specifies the size of the memory mapped buffer.
-After the calling the hello ioctl, you should memory map the kdbus
-fd. In this memory mapped region, the kernel will place all your incoming
-messages.
-
-SENDING MESSAGES
-
-Use the MSG_SEND ioctl to send a message to another peer. The ioctl
-takes a structure that contains a variety of fields:
-
-The flags field corresponds closely to the old dbus1 message header
-flags field, though the DONT_EXPECT_REPLY field got inverted into
-EXPECT_REPLY.
-
-The dst_id/src_id field contains the unique id of the destination and
-the sender. The sender field is overridden by the kernel usually, hence
-you shouldn't fill it in. The destination field can also take the
-special value KDBUS_DST_ID_BROADCAST for broadcast messages. For
-messages intended to a well-known name set the field to
-KDBUS_DST_ID_NAME, and attach the name in a special "items" entry to
-the message (see below).
-
-The payload field indicates the payload. For all dbus traffic it
-should carry the value 0x4442757344427573ULL. (Which encodes
-'DBusDBus').
-
-The cookie field corresponds with the "serial" field of classic
-dbus1. We simply renamed it here (and extended it to 64bit) since we
-didn't want to imply the monotonicity of the assignment the way the
-word "serial" indicates it.
-
-When sending a message that expects a reply, you need to set the
-EXPECT_REPLY flag in the message flag field. In this case you should
-also fill out the "timeout_ns" value which indicates the timeout in
-nsec for this call. If the peer does not respond in this time you will
-get a notification of a timeout. Note that this is also used for
-security purposes: a single reply messages is only allowed through the
-bus as long as the timeout has not ended. With this timeout value you
-hence "open a time window" in which the peer might respond to your
-request and the policy allows the response to go through.
-
-When sending a message that is a reply, you need to fill in the
-cookie_reply field, which is similar to the reply_serial field of
-dbus1. Note that a message cannot have EXPECT_REPLY and a reply_serial
-at the same time!
-
-This pretty much explains the ioctl header. The actual payload of the
-data is now referenced in additional items that are attached to this
-ioctl header structure at the end. When sending a message, you attach
-items of the type PAYLOAD_VEC, PAYLOAD_MEMFD, FDS, BLOOM, DST_NAME to
-it:
-
- KDBUS_ITEM_PAYLOAD_VEC: contains a pointer + length pair for
- referencing arbitrary user memory. This is how you reference most
- of your data. It's a lot like the good old iovec structure of glibc.
-
- KDBUS_ITEM_PAYLOAD_MEMFD: for large data blocks it is preferable
- to send prepared "memfds" (see below) over. This item contains an
- fd for a memfd plus a size.
-
- KDBUS_ITEM_PAYLOAD_FDS: for sending over fds attach an item of this
- type with an array of fds.
-
- KDBUS_ITEM_BLOOM: the calculated bloom filter of this message, only
- for undirected (broadcast) message.
-
- KDBUS_DST_NAME: for messages that are directed to a well-known name
- (instead of a unique name), this item contains the well-known name
- field.
-
-A single message may consists of no, one or more payload items of type
-PAYLOAD_VEC or PAYLOAD_MEMFD. D-Bus protocol implementations should
-treat them as a single block that just happens to be split up into
-multiple items. Some restrictions apply however:
-
- The message header in its entirety must be contained in a single
- PAYLOAD_VEC item.
-
- You may only split your message up right in front of each GVariant
- contained in the payload, as well is immediately before framing of a
- Gvariant, as well after as any padding bytes if there are any. The
- padding bytes must be wholly contained in the preceding
- PAYLOAD_VEC/PAYLOAD_MEMFD item. You may not split up simple types
- nor arrays of trivial types. The latter is necessary to allow APIs
- to return direct pointers to linear chunks of fixed size trivial
- arrays. Examples: The simple types "u", "s", "t" have to be in the
- same payload item. The array of simple types "ay", "ai" have to be
- fully in contained in the same payload item. For an array "as" or
- "a(si)" the only restriction however is to keep each string
- individually in an uninterrupted item, to keep the framing of each
- element and the array in a single uninterrupted item, however the
- various strings might end up in different items.
-
-Note again, that splitting up messages into separate items is up to the
-implementation. Also note that the kdbus kernel side might merge
-separate items if it deems this to be useful. However, the order in
-which items are contained in the message is left untouched.
-
-PAYLOAD_MEMFD items allow zero-copy data transfer (see below regarding
-the memfd concept). Note however that the overhead of mapping these
-makes them relatively expensive, and only worth the trouble for memory
-blocks > 128K (this value appears to be quite universal across
-architectures, as we tested). Thus we recommend sending PAYLOAD_VEC
-items over for small messages and restore to PAYLOAD_MEMFD items for
-messages > 128K. Since while building up the message you might not
-know yet whether it will grow beyond this boundary a good approach is
-to simply build the message unconditionally in a memfd
-object. However, when the message is sealed to be sent away check for
-the size limit. If the size of the message is < 128K, then simply send
-the data as PAYLOAD_VEC and reuse the memfd. If it is >= 128K, seal
-the memfd and send it as PAYLOAD_MEMFD, and allocate a new memfd for
-the next message.
-
-RECEIVING MESSAGES
-
-Use the MSG_RECV ioctl to read a message from kdbus. This will return
-an offset into the pool memory map, relative to its beginning.
-
-The received message structure more or less follows the structure of
-the message originally sent. However, certain changes have been
-made. In the header the src_id field will be filled in.
-
-The payload items might have gotten merged and PAYLOAD_VEC items are
-not used. Instead, you will only find PAYLOAD_OFF and PAYLOAD_MEMFD
-items. The former contain an offset and size into your memory mapped
-pool where you find the payload.
-
-If during the HELLO ioctl you asked for getting metadata attached to
-your message, you will find additional KDBUS_ITEM_CREDS,
-KDBUS_ITEM_PID_COMM, KDBUS_ITEM_TID_COMM, KDBUS_ITEM_TIMESTAMP,
-KDBUS_ITEM_EXE, KDBUS_ITEM_CMDLINE, KDBUS_ITEM_CGROUP,
-KDBUS_ITEM_CAPS, KDBUS_ITEM_SECLABEL, KDBUS_ITEM_AUDIT items that
-contain this metadata. This metadata will be gathered from the sender
-at the point in time it sends the message. This information is
-uncached, and since it is appended by the kernel, trustable. The
-KDBUS_ITEM_SECLABEL item usually contains the SELinux security label,
-if it is used.
-
-After processing the message you need to call the KDBUS_CMD_FREE
-ioctl, which releases the message from the pool, and allows the kernel
-to store another message there. Note that the memory used by the pool
-is ordinary anonymous, swappable memory that is backed by tmpfs. Hence
-there is no need to copy the message out of it quickly, instead you
-can just leave it there as long as you need it and release it via the
-FREE ioctl only after that's done.
-
-BLOOM FILTERS
-
-The kernel does not understand dbus marshaling, it will not look into
-the message payload. To allow clients to subscribe to specific subsets
-of the broadcast matches we employ bloom filters.
-
-When broadcasting messages, a bloom filter needs to be attached to the
-message in a KDBUS_ITEM_BLOOM item (and only for broadcasting
-messages!). If you don't know what bloom filters are, read up now on
-Wikipedia. In short: they are a very efficient way how to
-probabilistically check whether a certain word is contained in a
-vocabulary. It knows no false negatives, but it does know false
-positives.
-
-The bloom filter that needs to be included has the parameters m=512
-(bits in the filter), k=8 (nr of hash functions). The underlying hash
-function is SipHash-2-4. We calculate two hash values for an input
-strings, one with the hash key b9660bf0467047c18875c49c54b9bd15 (this
-is supposed to be read as a series of 16 hexadecimal formatted
-bytes), and one with the hash key
-aaa154a2e0714b39bfe1dd2e9fc54a3b. This results in two 64bit hash
-values, A and B. The 8 hash functions for the bloom filter require a 9
-bit output each (since m=512=2^9), to generate these we XOR combine
-the first 8 bit of A shifted to the left by 1, with the first 8 bit of
-B. Then, for the next hash function we use the second 8 bit pair, and
-so on.
-
-For each message to send across the bus we populate the bloom filter
-with all possible matchable strings. If a client then wants to
-subscribe to messages of this type, it simply tells the kernel to test
-its own calculated bit mask against the bloom filter of each message.
-
-More specifically, the following strings are added to the bloom filter
-of each message that is broadcasted:
-
- The string "interface:" suffixed by the interface name
-
- The string "member:" suffixed by the member name
-
- The string "path:" suffixed by the path name
-
- The string "path-slash-prefix:" suffixed with the path name, and
- also all prefixes of the path name (cut off at "/"), also prefixed
- with "path-slash-prefix".
-
- The string "message-type:" suffixed with the strings "signal",
- "method_call", "error" or "method_return" for the respective message
- type of the message.
-
- If the first argument of the message is a string, "arg0:" suffixed
- with the first argument.
-
- If the first argument of the message is a string, "arg0-dot-prefix"
- suffixed with the first argument, and also all prefixes of the
- argument (cut off at "."), also prefixed with "arg0-dot-prefix".
-
- If the first argument of the message is a string,
- "arg0-slash-prefix" suffixed with the first argument, and also all
- prefixes of the argument (cut off at "/"), also prefixed with
- "arg0-slash-prefix".
-
- Similar for all further arguments that are strings up to 63, for the
- arguments and their "dot" and "slash" prefixes. On the first
- argument that is not a string, addition to the bloom filter should be
- stopped however.
-
-(Note that the bloom filter does not contain sender nor receiver
-names!)
-
-When a client wants to subscribe to messages matching a certain
-expression, it should calculate the bloom mask following the same
-algorithm. The kernel will then simply test the mask against the
-attached bloom filters.
-
-Note that bloom filters are probabilistic, which means that clients
-might get messages they did not expect. Your bus protocol
-implementation must be capable of dealing with these unexpected
-messages (which it needs to anyway, given that transfers are
-relatively unrestricted on kdbus and people can send you all kinds of
-non-sense).
-
-INSTALLING MATCHES
-
-To install matches for broadcast messages, use the KDBUS_CMD_ADD_MATCH
-ioctl. It takes a structure that contains an encoded match expression,
-and that is followed by one or more items, which are combined in an
-AND way. (Meaning: a message is matched exactly when all items
-attached to the original ioctl struct match).
-
-To match against other user messages add a KDBUS_ITEM_BLOOM item in
-the match (see above). Note that the bloom filter does not include
-matches to the sender names. To additionally check against sender
-names, use the KDBUS_ITEM_ID (for unique id matches) and
-KDBUS_ITEM_NAME (for well-known name matches) item types.
-
-To match against kernel generated messages (see below) you should add
-items of the same type as the kernel messages include,
-i.e. KDBUS_ITEM_NAME_ADD, KDBUS_ITEM_NAME_REMOVE,
-KDBUS_ITEM_NAME_CHANGE, KDBUS_ITEM_ID_ADD, KDBUS_ITEM_ID_REMOVE and
-fill them out. Note however, that you have some wildcards in this
-case, for example the .id field of KDBUS_ITEM_ADD/KDBUS_ITEM_REMOVE
-structures may be set to 0 to match against any id addition/removal.
-
-Note that dbus match strings do no map 1:1 to these ioctl() calls. In
-many cases (where the match string is "underspecified") you might need
-to issue up to six different ioctl() calls for the same match. For
-example, the empty match (which matches against all messages), would
-translate into one KDBUS_ITEM_BLOOM ioctl, one KDBUS_ITEM_NAME_ADD,
-one KDBUS_ITEM_NAME_CHANGE, one KDBUS_ITEM_NAME_REMOVE, one
-KDBUS_ITEM_ID_ADD and one KDBUS_ITEM_ID_REMOVE.
-
-When creating a match, you may attach a "cookie" value to them, which
-is used for deleting this match again. The cookie can be selected freely
-by the client. When issuing KDBUS_CMD_REMOVE_MATCH, simply pass the
-same cookie as before and all matches matching the same "cookie" value
-will be removed. This is particularly handy for the case where multiple
-ioctl()s are added for a single match strings.
-
-MEMFDS
-
-The "memfd" concept is used for zero-copy data transfers (see
-above). memfds are file descriptors to memory chunks of arbitrary
-sizes. If you have a memfd you can mmap() it to get access to the data
-it contains or write to it. They are comparable to file descriptors to
-unlinked files on a tmpfs, or to anonymous memory that one may refer
-to with an fd. They have one particular property: they can be
-"sealed". A memfd that is "sealed" is protected from alteration. Only
-memfds that are currently not mapped and to which a single fd refers
-may be sealed (they may also be unsealed in that case).
-
-The concept of "sealing" makes memfds useful for using them as
-transport for kdbus messages: only when the receiver knows that the
-message it has received cannot change while looking at, it can safely
-parse it without having to copy it to a safe memory area. memfds can also
-be reused in multiple messages. A sender may send the same memfd to
-multiple peers, and since it is sealed, it can be sure that the receiver
-will not be able to modify it. "Sealing" hence provides both sides of
-a transaction with the guarantee that the data stays constant and is
-reusable.
-
-memfds are a generic concept that can be used outside of the immediate
-kdbus usecase. You can send them across AF_UNIX sockets too, sealed or
-unsealed. In kdbus themselves, they can be used to send zero-copy
-payloads, but may also be sent as normal fds.
-
-memfds are allocated with the KDBUS_CMD_MEMFD_NEW ioctl. After allocation,
-simply memory map them and write to them. To set their size, use
-KDBUS_CMD_MEMFD_SIZE_SET. Note that memfds will be increased in size
-automatically if you touch previously unallocated pages. However, the
-size will only be increased in multiples of the page size in that
-case. Thus, in almost all cases, an explicit KDBUS_CMD_MEMFD_SIZE_SET
-is necessary, since it allows setting memfd sizes in finer
-granularity. To seal a memfd use the KDBUS_CMD_MEMFD_SEAL_SET ioctl
-call. It will only succeed if the caller has the only fd reference to
-the memfd open, and if the memfd is currently unmapped.
-
-If memfds are shared, keep in mind that the file pointer used by
-write/read/seek is shared too, only pread/pwrite are safe to use
-in that case.
-
-memfds may be sent across kdbus via KDBUS_ITEM_PAYLOAD_MEMFD items
-attached to messages. If this is done, the data included in the memfd
-is considered part of the payload stream of a message, and are treated
-the same way as KDBUS_ITEM_PAYLOAD_VEC by the receiving side. It is
-possible to interleave KDBUS_ITEM_PAYLOAD_MEMFD and
-KDBUS_ITEM_PAYLOAD_VEC items freely, by the reader they will be
-considered a single stream of bytes in the order these items appear in
-the message, that just happens to be split up at various places
-(regarding rules how they may be split up, see above). The kernel will
-refuse taking KDBUS_ITEM_PAYLOAD_MEMFD items that refer to memfds that
-are not sealed.
-
-Note that sealed memfds may be unsealed again if they are not mapped
-you have the only fd reference to them.
-
-Alternatively to sending memfds as KDBUS_ITEM_PAYLOAD_MEMFD items
-(where they are just a part of the payload stream of a message) you can
-also simply attach any memfd to a message using
-KDBUS_ITEM_PAYLOAD_FDS. In this case, the memfd contents is not
-considered part of the payload stream of the message, but simply fds
-like any other, that happen to be attached to the message.
-
-MESSAGES FROM THE KERNEL
-
-A couple of messages previously generated by the dbus1 bus driver are
-now generated by the kernel. Since the kernel does not understand the
-payload marshaling, they are generated by the kernel in a different
-format. This is indicated with the "payload type" field of the
-messages set to 0. Library implementations should take these messages
-and synthesize traditional driver messages for them on reception.
-
-More specifically:
-
- Instead of the NameOwnerChanged, NameLost, NameAcquired signals
- there are kernel messages containing KDBUS_ITEM_NAME_ADD,
- KDBUS_ITEM_NAME_REMOVE, KDBUS_ITEM_NAME_CHANGE, KDBUS_ITEM_ID_ADD,
- KDBUS_ITEM_ID_REMOVE items are generated (each message will contain
- exactly one of these items). Note that in libsystemd we have
- obsoleted NameLost/NameAcquired messages, since they are entirely
- redundant to NameOwnerChanged. This library will hence only
- synthesize NameOwnerChanged messages from these kernel messages,
- and never generate NameLost/NameAcquired. If your library needs to
- stay compatible to the old dbus1 userspace, you possibly might need
- to synthesize both a NameOwnerChanged and NameLost/NameAcquired
- message from the same kernel message.
-
- When a method call times out, a KDBUS_ITEM_REPLY_TIMEOUT message is
- generated. This should be synthesized into a method error reply
- message to the original call.
-
- When a method call fails because the peer terminated the connection
- before responding, a KDBUS_ITEM_REPLY_DEAD message is
- generated. Similarly, it should be synthesized into a method error
- reply message.
-
-For synthesized messages we recommend setting the cookie field to
-(uint32_t) -1 (and not (uint64_t) -1!), so that the cookie is not 0
-(which the dbus1 spec does not allow), but clearly recognizable as
-synthetic.
-
-Note that the KDBUS_ITEM_NAME_XYZ messages will actually inform you
-about all kinds of names, including activatable ones. Classic dbus1
-NameOwnerChanged messages OTOH are only generated when a name is
-really acquired on the bus and not just simply activatable. This means
-you must explicitly check for the case where an activatable name
-becomes acquired or an acquired name is lost and returns to be
-activatable.
-
-NAME REGISTRY
-
-To acquire names on the bus, use the KDBUS_CMD_NAME_ACQUIRE ioctl(). It
-takes a flags field similar to dbus1's RequestName() bus driver call,
-however the NO_QUEUE flag got inverted into a QUEUE flag instead.
-
-To release a previously acquired name use the KDBUS_CMD_NAME_RELEASE
-ioctl().
-
-To list acquired names use the KDBUS_CMD_CONN_INFO ioctl. It may be
-used to list unique names, well known names as well as activatable
-names and clients currently queuing for ownership of a well-known
-name. The ioctl will return an offset into the memory pool. After
-reading all the data you need, you need to release this via the
-KDBUS_CMD_FREE ioctl(), similar how you release a received message.
-
-CREDENTIALS
-
-kdbus can optionally attach various kinds of metadata about the sender at
-the point of time of sending ("credentials") to messages, on request
-of the receiver. This is both supported on directed and undirected
-(broadcast) messages. The metadata to attach is selected at time of
-the HELLO ioctl of the receiver via a flags field (see above). Note
-that clients must be able to handle that messages contain more
-metadata than they asked for themselves, to simplify implementation of
-broadcasting in the kernel. The receiver should not rely on this data
-to be around though, even though it will be correct if it happens to
-be attached. In order to avoid programming errors in applications, we
-recommend though not passing this data on to clients that did not
-explicitly ask for it.
-
-Credentials may also be queried for a well-known or unique name. Use
-the KDBUS_CMD_CONN_INFO for this. It will return an offset to the pool
-area again, which will contain the same credential items as messages
-have attached. Note that when issuing the ioctl, you can select a
-different set of credentials to gather, than what was originally requested
-for being attached to incoming messages.
-
-Credentials are always specific to the sender namespace that was
-current at the time of sending, and of the process that opened the
-bus connection at the time of opening it. Note that this latter data
-is cached!
-
-POLICY
-
-The kernel enforces only very limited policy on names. It will not do
-access filtering by userspace payload, and thus not by interface or
-method name.
-
-This ultimately means that most fine-grained policy enforcement needs
-to be done by the receiving process. We recommend using PolicyKit for
-any more complex checks. However, libraries should make simple static
-policy decisions regarding privileged/unprivileged method calls
-easy. We recommend doing this by enabling KDBUS_ATTACH_CAPS and
-KDBUS_ATTACH_CREDS for incoming messages, and then discerning client
-access by some capability, or if sender and receiver UIDs match.
-
-BUS ADDRESSES
-
-When connecting to kdbus use the "kernel:" protocol prefix in DBus
-address strings. The device node path is encoded in its "path="
-parameter.
-
-Client libraries should use the following connection string when
-connecting to the system bus:
-
- kernel:path=/dev/kdbus/0-system/bus;unix:path=/run/dbus/system_bus_socket
-
-This will ensure that kdbus is preferred over the legacy AF_UNIX
-socket, but compatibility is kept. For the user bus use:
-
- kernel:path=/dev/kdbus/$UID-user/bus;unix:path=$XDG_RUNTIME_DIR/bus
-
-With $UID replaced by the callers numer user ID, and $XDG_RUNTIME_DIR
-following the XDG basedir spec.
-
-Of course the $DBUS_SYSTEM_BUS_ADDRESS and $DBUS_SESSION_BUS_ADDRESS
-variables should still take precedence.
-
-DBUS SERVICE FILES
-
-Activatable services for kdbus may not use classic dbus1 service
-activation files. Instead, programs should drop in native systemd
-.service and .busname unit files, so that they are treated uniformly
-with other types of units and activation of the system.
-
-Note that this results in a major difference to classic dbus1:
-activatable bus names can be established at any time in the boot process.
-This is unlike dbus1 where activatable names are unconditionally available
-as long as dbus-daemon is running. Being able to control when
-activatable names are established is essential to allow usage of kdbus
-during early boot and in initrds, without the risk of triggering
-services too early.
-
-DISCLAIMER
-
-This all is so far just the status quo. We are putting this together, because
-we are quite confident that further API changes will be smaller, but
-to make this very clear: this is all subject to change, still!
-
-We invite you to port over your favorite dbus library to this new
-scheme, but please be prepared to make minor changes when we still
-change these interfaces!
diff --git a/src/libsystemd/bus-bloom.c b/src/libsystemd/bus-bloom.c
deleted file mode 100644
index 9e51334..0000000
--- a/src/libsystemd/bus-bloom.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-#include "siphash24.h"
-#include "bus-bloom.h"
-
-static inline void set_bit(uint64_t filter[], unsigned b) {
- filter[b >> 6] |= 1ULL << (b & 63);
-}
-
-#define HASH_KEY1 SD_ID128_MAKE(b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15)
-#define HASH_KEY2 SD_ID128_MAKE(aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b)
-
-static void bloom_add_data(uint64_t filter[BLOOM_SIZE/8], const void *data, size_t n) {
- uint8_t a[8], b[8];
- unsigned k = 0;
-
- /*
- * Our bloom filter has the following parameters:
- *
- * m=512 (bits in the filter)
- * k=8 (hash functions)
- *
- * We calculate a two 64bit SipHash values (for two fixed but
- * randomly generated hash keys) of which we use 8 parts of 9 bits
- * as individual hash functions.
- *
- */
-
- siphash24(a, data, n, HASH_KEY1.bytes);
- siphash24(b, data, n, HASH_KEY2.bytes);
-
- assert_cc(BLOOM_SIZE*8 == 512);
-
- for (k = 0; k < 8; k++)
- set_bit(filter, ((uint16_t) a[k] << 1) ^ (uint16_t) b[k]);
-
- /* log_debug("bloom: adding <%.*s>", (int) n, (char*) data); */
-}
-
-void bloom_add_pair(uint64_t filter[BLOOM_SIZE/8], const char *a, const char *b) {
- size_t n;
- char *c;
-
- assert(filter);
- assert(a);
- assert(b);
-
- n = strlen(a) + 1 + strlen(b);
- c = alloca(n + 1);
- strcpy(stpcpy(stpcpy(c, a), ":"), b);
-
- bloom_add_data(filter, c, n);
-}
-
-void bloom_add_prefixes(uint64_t filter[BLOOM_SIZE/8], const char *a, const char *b, char sep) {
- size_t n;
- char *c, *p;
-
- assert(filter);
- assert(a);
- assert(b);
-
- n = strlen(a) + 1 + strlen(b);
- c = alloca(n + 1);
-
- p = stpcpy(stpcpy(c, a), ":");
- strcpy(p, b);
-
- for (;;) {
- char *e;
-
- e = strrchr(p, sep);
- if (!e || e == p)
- break;
-
- *e = 0;
- bloom_add_data(filter, c, e - c);
- }
-}
diff --git a/src/libsystemd/bus-bloom.h b/src/libsystemd/bus-bloom.h
deleted file mode 100644
index 422eb4e..0000000
--- a/src/libsystemd/bus-bloom.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/types.h>
-
-#define BLOOM_SIZE 64
-
-void bloom_add_pair(uint64_t filter[BLOOM_SIZE/8], const char *a, const char *b);
-void bloom_add_prefixes(uint64_t filter[BLOOM_SIZE/8], const char *a, const char *b, char sep);
diff --git a/src/libsystemd/bus-container.c b/src/libsystemd/bus-container.c
deleted file mode 100644
index d330363..0000000
--- a/src/libsystemd/bus-container.c
+++ /dev/null
@@ -1,241 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "util.h"
-#include "fileio.h"
-#include "bus-internal.h"
-#include "bus-socket.h"
-#include "bus-container.h"
-
-int bus_container_connect_socket(sd_bus *b) {
- _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
- pid_t leader, child;
- siginfo_t si;
- int r;
-
- assert(b);
- assert(b->input_fd < 0);
- assert(b->output_fd < 0);
-
- r = container_get_leader(b->machine, &leader);
- if (r < 0)
- return r;
-
- r = namespace_open(leader, &pidnsfd, &mntnsfd, &rootfd);
- if (r < 0)
- return r;
-
- b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (b->input_fd < 0)
- return -errno;
-
- b->output_fd = b->input_fd;
-
- bus_socket_setup(b);
-
- child = fork();
- if (child < 0)
- return -errno;
-
- if (child == 0) {
- pid_t grandchild;
-
- r = namespace_enter(pidnsfd, mntnsfd, rootfd);
- if (r < 0)
- _exit(255);
-
- /* We just changed PID namespace, however it will only
- * take effect on the children we now fork. Hence,
- * let's fork another time, and connect from this
- * grandchild, so that SO_PEERCRED of our connection
- * comes from a process from within the container, and
- * not outside of it */
-
- grandchild = fork();
- if (grandchild < 0)
- _exit(255);
-
- if (grandchild == 0) {
-
- r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
- if (r < 0) {
- if (errno == EINPROGRESS)
- _exit(1);
-
- _exit(255);
- }
-
- _exit(EXIT_SUCCESS);
- }
-
- r = wait_for_terminate(grandchild, &si);
- if (r < 0)
- _exit(255);
-
- if (si.si_code != CLD_EXITED)
- _exit(255);
-
- _exit(si.si_status);
- }
-
- r = wait_for_terminate(child, &si);
- if (r < 0)
- return r;
-
- if (si.si_code != CLD_EXITED)
- return -EIO;
-
- if (si.si_status == 1)
- return 1;
-
- if (si.si_status != EXIT_SUCCESS)
- return -EIO;
-
- return bus_socket_start_auth(b);
-}
-
-int bus_container_connect_kernel(sd_bus *b) {
- _cleanup_close_pipe_ int pair[2] = { -1, -1 };
- _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
- pid_t leader, child;
- siginfo_t si;
- int r;
- _cleanup_close_ int fd = -1;
-
- assert(b);
- assert(b->input_fd < 0);
- assert(b->output_fd < 0);
-
- r = container_get_leader(b->machine, &leader);
- if (r < 0)
- return r;
-
- r = namespace_open(leader, &pidnsfd, &mntnsfd, &rootfd);
- if (r < 0)
- return r;
-
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
- return -errno;
-
- child = fork();
- if (child < 0)
- return -errno;
-
- if (child == 0) {
- pid_t grandchild;
-
- close_nointr_nofail(pair[0]);
- pair[0] = -1;
-
- r = namespace_enter(pidnsfd, mntnsfd, rootfd);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
- /* We just changed PID namespace, however it will only
- * take effect on the children we now fork. Hence,
- * let's fork another time, and connect from this
- * grandchild, so that kdbus only sees the credentials
- * of this process which comes from within the
- * container, and not outside of it */
-
- grandchild = fork();
- if (grandchild < 0)
- _exit(EXIT_FAILURE);
-
- if (grandchild == 0) {
-
- fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- _exit(EXIT_FAILURE);
-
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
-
- mh.msg_controllen = cmsg->cmsg_len;
-
- if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
- _exit(EXIT_FAILURE);
-
- _exit(EXIT_SUCCESS);
- }
-
- r = wait_for_terminate(grandchild, &si);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
- if (si.si_code != CLD_EXITED)
- _exit(EXIT_FAILURE);
-
- _exit(si.si_status);
- }
-
- close_nointr_nofail(pair[1]);
- pair[1] = -1;
-
- r = wait_for_terminate(child, &si);
- if (r < 0)
- return r;
-
- if (si.si_code != CLD_EXITED)
- return -EIO;
-
- if (si.si_status != EXIT_SUCCESS)
- return -EIO;
-
- if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
- return -errno;
-
- for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
- int *fds;
- unsigned n_fds;
-
- fds = (int*) CMSG_DATA(cmsg);
- n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
-
- if (n_fds != 1) {
- close_many(fds, n_fds);
- return -EIO;
- }
-
- fd = fds[0];
- }
-
- b->input_fd = b->output_fd = fd;
- fd = -1;
-
- return bus_kernel_take_fd(b);
-}
diff --git a/src/libsystemd/bus-container.h b/src/libsystemd/bus-container.h
deleted file mode 100644
index c6f757a..0000000
--- a/src/libsystemd/bus-container.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "sd-bus.h"
-
-int bus_container_connect_socket(sd_bus *b);
-int bus_container_connect_kernel(sd_bus *b);
diff --git a/src/libsystemd/bus-control.c b/src/libsystemd/bus-control.c
deleted file mode 100644
index e7e9ba0..0000000
--- a/src/libsystemd/bus-control.c
+++ /dev/null
@@ -1,1217 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#ifdef HAVE_VALGRIND_MEMCHECK_H
-#include <valgrind/memcheck.h>
-#endif
-
-#include <stddef.h>
-#include <errno.h>
-
-#include "strv.h"
-#include "sd-bus.h"
-#include "bus-internal.h"
-#include "bus-message.h"
-#include "bus-control.h"
-#include "bus-bloom.h"
-#include "bus-util.h"
-#include "cgroup-util.h"
-
-_public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(unique, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = bus_ensure_running(bus);
- if (r < 0)
- return r;
-
- *unique = bus->unique_name;
- return 0;
-}
-
-static int bus_request_name_kernel(sd_bus *bus, const char *name, uint64_t flags) {
- struct kdbus_cmd_name *n;
- size_t size, l;
- int r;
-
- assert(bus);
- assert(name);
-
- l = strlen(name);
- size = offsetof(struct kdbus_cmd_name, name) + l + 1;
- n = alloca0(size);
- n->size = size;
- kdbus_translate_request_name_flags(flags, (uint64_t *) &n->flags);
- memcpy(n->name, name, l+1);
-
-#ifdef HAVE_VALGRIND_MEMCHECK_H
- VALGRIND_MAKE_MEM_DEFINED(n, n->size);
-#endif
-
- r = ioctl(bus->input_fd, KDBUS_CMD_NAME_ACQUIRE, n);
- if (r < 0)
- return -errno;
-
- if (n->flags & KDBUS_NAME_IN_QUEUE)
- return 0;
-
- return 1;
-}
-
-static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- uint32_t ret, param = 0;
- int r;
-
- assert(bus);
- assert(name);
-
- if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT)
- param |= BUS_NAME_ALLOW_REPLACEMENT;
- if (flags & SD_BUS_NAME_REPLACE_EXISTING)
- param |= BUS_NAME_REPLACE_EXISTING;
- if (!(flags & SD_BUS_NAME_QUEUE))
- param |= BUS_NAME_DO_NOT_QUEUE;
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "RequestName",
- NULL,
- &reply,
- "su",
- name,
- param);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(reply, "u", &ret);
- if (r < 0)
- return r;
-
- if (ret == BUS_NAME_ALREADY_OWNER)
- return -EALREADY;
- else if (ret == BUS_NAME_EXISTS)
- return -EEXIST;
- else if (ret == BUS_NAME_IN_QUEUE)
- return 0;
- else if (ret == BUS_NAME_PRIMARY_OWNER)
- return 1;
-
- return -EIO;
-}
-
-_public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) {
- assert_return(bus, -EINVAL);
- assert_return(name, -EINVAL);
- assert_return(bus->bus_client, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL);
- assert_return(service_name_is_valid(name), -EINVAL);
- assert_return(name[0] != ':', -EINVAL);
-
- if (bus->is_kernel)
- return bus_request_name_kernel(bus, name, flags);
- else
- return bus_request_name_dbus1(bus, name, flags);
-}
-
-static int bus_release_name_kernel(sd_bus *bus, const char *name) {
- struct kdbus_cmd_name *n;
- size_t l;
- int r;
-
- assert(bus);
- assert(name);
-
- l = strlen(name);
- n = alloca0(offsetof(struct kdbus_cmd_name, name) + l + 1);
- n->size = offsetof(struct kdbus_cmd_name, name) + l + 1;
- memcpy(n->name, name, l+1);
-
-#ifdef HAVE_VALGRIND_MEMCHECK_H
- VALGRIND_MAKE_MEM_DEFINED(n, n->size);
-#endif
- r = ioctl(bus->input_fd, KDBUS_CMD_NAME_RELEASE, n);
- if (r < 0)
- return -errno;
-
- return n->flags;
-}
-
-static int bus_release_name_dbus1(sd_bus *bus, const char *name) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- uint32_t ret;
- int r;
-
- assert(bus);
- assert(name);
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "ReleaseName",
- NULL,
- &reply,
- "s",
- name);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(reply, "u", &ret);
- if (r < 0)
- return r;
- if (ret == BUS_NAME_NON_EXISTENT)
- return -ESRCH;
- if (ret == BUS_NAME_NOT_OWNER)
- return -EADDRINUSE;
- if (ret == BUS_NAME_RELEASED)
- return 0;
-
- return -EINVAL;
-}
-
-_public_ int sd_bus_release_name(sd_bus *bus, const char *name) {
- assert_return(bus, -EINVAL);
- assert_return(name, -EINVAL);
- assert_return(bus->bus_client, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(service_name_is_valid(name), -EINVAL);
- assert_return(name[0] != ':', -EINVAL);
-
- if (bus->is_kernel)
- return bus_release_name_kernel(bus, name);
- else
- return bus_release_name_dbus1(bus, name);
-}
-
-static int kernel_get_list(sd_bus *bus, uint64_t flags, char ***x) {
- struct kdbus_cmd_name_list cmd = {};
- struct kdbus_name_list *name_list;
- struct kdbus_cmd_name *name;
- uint64_t previous_id = 0;
- int r;
-
- /* Caller will free half-constructed list on failure... */
-
- cmd.flags = flags;
-
- r = ioctl(bus->input_fd, KDBUS_CMD_NAME_LIST, &cmd);
- if (r < 0)
- return -errno;
-
- name_list = (struct kdbus_name_list *) ((uint8_t *) bus->kdbus_buffer + cmd.offset);
-
- KDBUS_ITEM_FOREACH(name, name_list, names) {
-
- if ((flags & KDBUS_NAME_LIST_UNIQUE) && name->owner_id != previous_id) {
- char *n;
-
- if (asprintf(&n, ":1.%llu", (unsigned long long) name->owner_id) < 0)
- return -ENOMEM;
-
- r = strv_push(x, n);
- if (r < 0) {
- free(n);
- return -ENOMEM;
- }
-
- previous_id = name->owner_id;
- }
-
- if (name->size > sizeof(*name) && service_name_is_valid(name->name)) {
- r = strv_extend(x, name->name);
- if (r < 0)
- return -ENOMEM;
- }
- }
-
- r = ioctl(bus->input_fd, KDBUS_CMD_FREE, &cmd.offset);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-static int bus_list_names_kernel(sd_bus *bus, char ***acquired, char ***activatable) {
- _cleanup_strv_free_ char **x = NULL, **y = NULL;
- int r;
-
- if (acquired) {
- r = kernel_get_list(bus, KDBUS_NAME_LIST_UNIQUE | KDBUS_NAME_LIST_NAMES, &x);
- if (r < 0)
- return r;
- }
-
- if (activatable) {
- r = kernel_get_list(bus, KDBUS_NAME_LIST_ACTIVATORS, &y);
- if (r < 0)
- return r;
-
- *activatable = y;
- y = NULL;
- }
-
- if (acquired) {
- *acquired = x;
- x = NULL;
- }
-
- return 0;
-}
-
-static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatable) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_strv_free_ char **x = NULL, **y = NULL;
- int r;
-
- if (acquired) {
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "ListNames",
- NULL,
- &reply,
- NULL);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_strv(reply, &x);
- if (r < 0)
- return r;
-
- reply = sd_bus_message_unref(reply);
- }
-
- if (activatable) {
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "ListActivatableNames",
- NULL,
- &reply,
- NULL);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_strv(reply, &y);
- if (r < 0)
- return r;
-
- *activatable = y;
- y = NULL;
- }
-
- if (acquired) {
- *acquired = x;
- x = NULL;
- }
-
- return 0;
-}
-
-_public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) {
- assert_return(bus, -EINVAL);
- assert_return(acquired || activatable, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (bus->is_kernel)
- return bus_list_names_kernel(bus, acquired, activatable);
- else
- return bus_list_names_dbus1(bus, acquired, activatable);
-}
-
-static int bus_get_owner_kdbus(
- sd_bus *bus,
- const char *name,
- uint64_t mask,
- sd_bus_creds **creds) {
-
- _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
- struct kdbus_cmd_conn_info *cmd;
- struct kdbus_conn_info *conn_info;
- struct kdbus_item *item;
- size_t size;
- uint64_t m, id;
- int r;
-
- r = bus_kernel_parse_unique_name(name, &id);
- if (r < 0)
- return r;
- if (r > 0) {
- size = offsetof(struct kdbus_cmd_conn_info, name);
- cmd = alloca0(size);
- cmd->id = id;
- } else {
- size = offsetof(struct kdbus_cmd_conn_info, name) + strlen(name) + 1;
- cmd = alloca0(size);
- strcpy(cmd->name, name);
- }
- cmd->flags = KDBUS_ATTACH_NAMES;
-
- cmd->size = size;
- r = ioctl(bus->input_fd, KDBUS_CMD_CONN_INFO, cmd);
- if (r < 0)
- return -errno;
-
- conn_info = (struct kdbus_conn_info *) ((uint8_t *) bus->kdbus_buffer + cmd->offset);
-
- /* Non-activated names are considered not available */
- if (conn_info->flags & KDBUS_HELLO_ACTIVATOR)
- return name[0] == ':' ? -ENXIO : -ENOENT;
-
- c = bus_creds_new();
- if (!c)
- return -ENOMEM;
-
- if (mask & SD_BUS_CREDS_UNIQUE_NAME) {
- if (asprintf(&c->unique_name, ":1.%llu", (unsigned long long) conn_info->id) < 0)
- return -ENOMEM;
-
- c->mask |= SD_BUS_CREDS_UNIQUE_NAME;
- }
-
- KDBUS_ITEM_FOREACH(item, conn_info, items) {
-
- switch (item->type) {
-
- case KDBUS_ITEM_CREDS:
- m = (SD_BUS_CREDS_UID | SD_BUS_CREDS_GID | SD_BUS_CREDS_PID) & mask;
-
- if (m) {
- c->uid = (uid_t) item->creds.uid;
- c->pid = (pid_t) item->creds.pid;
- c->gid = (gid_t) item->creds.gid;
- c->mask |= m;
- }
-
- if (mask & SD_BUS_CREDS_TID && item->creds.tid > 0) {
- c->tid = (pid_t) item->creds.tid;
- c->mask |= SD_BUS_CREDS_TID;
- }
-
- if (mask & SD_BUS_CREDS_PID_STARTTIME && item->creds.starttime > 0) {
- c->pid_starttime = item->creds.starttime;
- c->mask |= SD_BUS_CREDS_PID_STARTTIME;
- }
-
- break;
-
- case KDBUS_ITEM_PID_COMM:
- if (mask & SD_BUS_CREDS_COMM) {
- c->comm = strdup(item->str);
- if (!c->comm) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->mask |= SD_BUS_CREDS_COMM;
- }
- break;
-
- case KDBUS_ITEM_TID_COMM:
- if (mask & SD_BUS_CREDS_TID_COMM) {
- c->tid_comm = strdup(item->str);
- if (!c->tid_comm) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->mask |= SD_BUS_CREDS_TID_COMM;
- }
- break;
-
- case KDBUS_ITEM_EXE:
- if (mask & SD_BUS_CREDS_EXE) {
- c->exe = strdup(item->str);
- if (!c->exe) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->mask |= SD_BUS_CREDS_EXE;
- }
- break;
-
- case KDBUS_ITEM_CMDLINE:
- if (mask & SD_BUS_CREDS_CMDLINE) {
- c->cmdline_size = item->size - KDBUS_ITEM_HEADER_SIZE;
- c->cmdline = memdup(item->data, c->cmdline_size);
- if (!c->cmdline) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->mask |= SD_BUS_CREDS_CMDLINE;
- }
- break;
-
- case KDBUS_ITEM_CGROUP:
- m = (SD_BUS_CREDS_CGROUP | SD_BUS_CREDS_UNIT |
- SD_BUS_CREDS_USER_UNIT | SD_BUS_CREDS_SLICE |
- SD_BUS_CREDS_SESSION | SD_BUS_CREDS_OWNER_UID) & mask;
-
- if (m) {
- c->cgroup = strdup(item->str);
- if (!c->cgroup) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (!bus->cgroup_root) {
- r = cg_get_root_path(&bus->cgroup_root);
- if (r < 0)
- goto fail;
- }
-
- c->cgroup_root = strdup(bus->cgroup_root);
- if (!c->cgroup_root) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->mask |= m;
- }
- break;
-
- case KDBUS_ITEM_CAPS:
- m = (SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_PERMITTED_CAPS |
- SD_BUS_CREDS_INHERITABLE_CAPS | SD_BUS_CREDS_BOUNDING_CAPS) & mask;
-
- if (m) {
- c->capability_size = item->size - KDBUS_ITEM_HEADER_SIZE;
- c->capability = memdup(item->data, c->capability_size);
- if (!c->capability) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->mask |= m;
- }
- break;
-
- case KDBUS_ITEM_SECLABEL:
- if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
- c->label = strdup(item->str);
- if (!c->label) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
- }
- break;
-
- case KDBUS_ITEM_AUDIT:
- m = (SD_BUS_CREDS_AUDIT_SESSION_ID | SD_BUS_CREDS_AUDIT_LOGIN_UID) & mask;
-
- if (m) {
- c->audit_session_id = item->audit.sessionid;
- c->audit_login_uid = item->audit.loginuid;
- c->mask |= m;
- }
- break;
-
- case KDBUS_ITEM_NAME:
- if ((mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) && service_name_is_valid(item->name.name)) {
- r = strv_extend(&c->well_known_names, item->name.name);
- if (r < 0)
- goto fail;
-
- c->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES;
- }
- break;
- }
- }
-
- if (creds) {
- *creds = c;
- c = NULL;
- }
-
- r = 0;
-
-fail:
- ioctl(bus->input_fd, KDBUS_CMD_FREE, &cmd->offset);
- return r;
-}
-
-static int bus_get_owner_dbus1(
- sd_bus *bus,
- const char *name,
- uint64_t mask,
- sd_bus_creds **creds) {
-
- _cleanup_bus_message_unref_ sd_bus_message *reply_unique = NULL, *reply = NULL;
- _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
- const char *unique = NULL;
- pid_t pid = 0;
- int r;
-
- /* Only query the owner if the caller wants to know it or if
- * the caller just wants to check whether a name exists */
- if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) {
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "GetNameOwner",
- NULL,
- &reply_unique,
- "s",
- name);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(reply_unique, "s", &unique);
- if (r < 0)
- return r;
- }
-
- if (mask != 0) {
- c = bus_creds_new();
- if (!c)
- return -ENOMEM;
-
- if ((mask & SD_BUS_CREDS_UNIQUE_NAME) && unique) {
- c->unique_name = strdup(unique);
- if (!c->unique_name)
- return -ENOMEM;
-
- c->mask |= SD_BUS_CREDS_UNIQUE_NAME;
- }
-
- if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_GID|
- SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
- SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|
- SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
- SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)) {
- uint32_t u;
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "GetConnectionUnixProcessID",
- NULL,
- &reply,
- "s",
- unique ? unique : name);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(reply, "u", &u);
- if (r < 0)
- return r;
-
- pid = u;
- if (mask & SD_BUS_CREDS_PID) {
- c->pid = u;
- c->mask |= SD_BUS_CREDS_PID;
- }
-
- reply = sd_bus_message_unref(reply);
- }
-
- if (mask & SD_BUS_CREDS_UID) {
- uint32_t u;
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "GetConnectionUnixUser",
- NULL,
- &reply,
- "s",
- unique ? unique : name);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(reply, "u", &u);
- if (r < 0)
- return r;
-
- c->uid = u;
- c->mask |= SD_BUS_CREDS_UID;
-
- reply = sd_bus_message_unref(reply);
- }
-
- if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
- const void *p;
- size_t sz;
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "GetConnectionSELinuxSecurityContext",
- NULL,
- &reply,
- "s",
- unique ? unique : name);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_array(reply, 'y', &p, &sz);
- if (r < 0)
- return r;
-
- c->label = strndup(p, sz);
- if (!c->label)
- return -ENOMEM;
-
- c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
- }
-
- r = bus_creds_add_more(c, mask, pid, 0);
- if (r < 0)
- return r;
- }
-
- if (creds) {
- *creds = c;
- c = NULL;
- }
-
- return 0;
-}
-
-_public_ int sd_bus_get_owner(
- sd_bus *bus,
- const char *name,
- uint64_t mask,
- sd_bus_creds **creds) {
-
- assert_return(bus, -EINVAL);
- assert_return(name, -EINVAL);
- assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP);
- assert_return(mask == 0 || creds, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(service_name_is_valid(name), -EINVAL);
- assert_return(bus->bus_client, -ENODATA);
-
- if (bus->is_kernel)
- return bus_get_owner_kdbus(bus, name, mask, creds);
- else
- return bus_get_owner_dbus1(bus, name, mask, creds);
-}
-
-static int add_name_change_match(sd_bus *bus,
- uint64_t cookie,
- const char *name,
- const char *old_owner,
- const char *new_owner) {
-
- uint64_t name_id = KDBUS_MATCH_ID_ANY, old_owner_id = 0, new_owner_id = 0;
- int is_name_id = -1, r;
- struct kdbus_item *item;
-
- assert(bus);
-
- /* If we encounter a match that could match against
- * NameOwnerChanged messages, then we need to create
- * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE} and
- * KDBUS_ITEM_ID_{ADD,REMOVE} matches for it, possibly
- * multiple if the match is underspecified.
- *
- * The NameOwnerChanged signals take three parameters with
- * unique or well-known names, but only some forms actually
- * exist:
- *
- * WELLKNOWN, "", UNIQUE â KDBUS_ITEM_NAME_ADD
- * WELLKNOWN, UNIQUE, "" â KDBUS_ITEM_NAME_REMOVE
- * WELLKNOWN, UNIQUE, UNIQUE â KDBUS_ITEM_NAME_CHANGE
- * UNIQUE, "", UNIQUE â KDBUS_ITEM_ID_ADD
- * UNIQUE, UNIQUE, "" â KDBUS_ITEM_ID_REMOVE
- *
- * For the latter two the two unique names must be identical.
- *
- * */
-
- if (name) {
- is_name_id = bus_kernel_parse_unique_name(name, &name_id);
- if (is_name_id < 0)
- return 0;
- }
-
- if (!isempty(old_owner)) {
- r = bus_kernel_parse_unique_name(old_owner, &old_owner_id);
- if (r < 0)
- return 0;
- if (r == 0)
- return 0;
- if (is_name_id > 0 && old_owner_id != name_id)
- return 0;
- } else
- old_owner_id = KDBUS_MATCH_ID_ANY;
-
- if (!isempty(new_owner)) {
- r = bus_kernel_parse_unique_name(new_owner, &new_owner_id);
- if (r < 0)
- return r;
- if (r == 0)
- return 0;
- if (is_name_id > 0 && new_owner_id != name_id)
- return 0;
- } else
- new_owner_id = KDBUS_MATCH_ID_ANY;
-
- if (is_name_id <= 0) {
- struct kdbus_cmd_match *m;
- size_t sz, l;
-
- /* If the name argument is missing or is a well-known
- * name, then add KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}
- * matches for it */
-
- l = name ? strlen(name) + 1 : 0;
-
- sz = ALIGN8(offsetof(struct kdbus_cmd_match, items) +
- offsetof(struct kdbus_item, name_change) +
- offsetof(struct kdbus_notify_name_change, name) +
- l);
-
- m = alloca0(sz);
- m->size = sz;
- m->cookie = cookie;
-
- item = m->items;
- item->size =
- offsetof(struct kdbus_item, name_change) +
- offsetof(struct kdbus_notify_name_change, name) +
- l;
-
- item->name_change.old.id = old_owner_id;
- item->name_change.new.id = new_owner_id;
-
- if (name)
- memcpy(item->name_change.name, name, l);
-
- /* If the old name is unset or empty, then
- * this can match against added names */
- if (!old_owner || old_owner[0] == 0) {
- item->type = KDBUS_ITEM_NAME_ADD;
-
- r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
- if (r < 0)
- return -errno;
- }
-
- /* If the new name is unset or empty, then
- * this can match against removed names */
- if (!new_owner || new_owner[0] == 0) {
- item->type = KDBUS_ITEM_NAME_REMOVE;
-
- r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
- if (r < 0)
- return -errno;
- }
-
- /* The CHANGE match we need in either case, because
- * what is reported as a name change by the kernel
- * might just be an owner change between starter and
- * normal clients. For userspace such a change should
- * be considered a removal/addition, hence let's
- * subscribe to this unconditionally. */
- item->type = KDBUS_ITEM_NAME_CHANGE;
- r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
- if (r < 0)
- return -errno;
- }
-
- if (is_name_id != 0) {
- struct kdbus_cmd_match *m;
- uint64_t sz;
-
- /* If the name argument is missing or is a unique
- * name, then add KDBUS_ITEM_ID_{ADD,REMOVE} matches
- * for it */
-
- sz = ALIGN8(offsetof(struct kdbus_cmd_match, items) +
- offsetof(struct kdbus_item, id_change) +
- sizeof(struct kdbus_notify_id_change));
-
- m = alloca0(sz);
- m->size = sz;
- m->cookie = cookie;
-
- item = m->items;
- item->size =
- offsetof(struct kdbus_item, id_change) +
- sizeof(struct kdbus_notify_id_change);
- item->id_change.id = name_id;
-
- /* If the old name is unset or empty, then this can
- * match against added ids */
- if (!old_owner || old_owner[0] == 0) {
- item->type = KDBUS_ITEM_ID_ADD;
-
- r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
- if (r < 0)
- return -errno;
- }
-
- /* If thew new name is unset or empty, then this can
- * match against removed ids */
- if (!new_owner || new_owner[0] == 0) {
- item->type = KDBUS_ITEM_ID_REMOVE;
-
- r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
- if (r < 0)
- return -errno;
- }
- }
-
- return 0;
-}
-
-int bus_add_match_internal_kernel(
- sd_bus *bus,
- uint64_t id,
- struct bus_match_component *components,
- unsigned n_components,
- uint64_t cookie) {
-
- struct kdbus_cmd_match *m;
- struct kdbus_item *item;
- uint64_t bloom[BLOOM_SIZE/8];
- size_t sz;
- const char *sender = NULL;
- size_t sender_length = 0;
- uint64_t src_id = KDBUS_MATCH_ID_ANY;
- bool using_bloom = false;
- unsigned i;
- bool matches_name_change = true;
- const char *name_change_arg[3] = {};
- int r;
-
- assert(bus);
-
- zero(bloom);
-
- sz = ALIGN8(offsetof(struct kdbus_cmd_match, items));
-
- for (i = 0; i < n_components; i++) {
- struct bus_match_component *c = &components[i];
-
- switch (c->type) {
-
- case BUS_MATCH_SENDER:
- if (!streq(c->value_str, "org.freedesktop.DBus"))
- matches_name_change = false;
-
- r = bus_kernel_parse_unique_name(c->value_str, &src_id);
- if (r < 0)
- return r;
- else if (r > 0)
- sz += ALIGN8(offsetof(struct kdbus_item, id) + sizeof(uint64_t));
- else {
- sender = c->value_str;
- sender_length = strlen(sender);
- sz += ALIGN8(offsetof(struct kdbus_item, str) + sender_length + 1);
- }
-
- break;
-
- case BUS_MATCH_MESSAGE_TYPE:
- if (c->value_u8 != SD_BUS_MESSAGE_SIGNAL)
- matches_name_change = false;
-
- bloom_add_pair(bloom, "message-type", bus_message_type_to_string(c->value_u8));
- using_bloom = true;
- break;
-
- case BUS_MATCH_INTERFACE:
- if (!streq(c->value_str, "org.freedesktop.DBus"))
- matches_name_change = false;
-
- bloom_add_pair(bloom, "interface", c->value_str);
- using_bloom = true;
- break;
-
- case BUS_MATCH_MEMBER:
- if (!streq(c->value_str, "NameOwnerChanged"))
- matches_name_change = false;
-
- bloom_add_pair(bloom, "member", c->value_str);
- using_bloom = true;
- break;
-
- case BUS_MATCH_PATH:
- if (!streq(c->value_str, "/org/freedesktop/DBus"))
- matches_name_change = false;
-
- bloom_add_pair(bloom, "path", c->value_str);
- using_bloom = true;
- break;
-
- case BUS_MATCH_PATH_NAMESPACE:
- if (!streq(c->value_str, "/")) {
- bloom_add_pair(bloom, "path-slash-prefix", c->value_str);
- using_bloom = true;
- }
- break;
-
- case BUS_MATCH_ARG...BUS_MATCH_ARG_LAST: {
- char buf[sizeof("arg")-1 + 2 + 1];
-
- if (c->type - BUS_MATCH_ARG < 3)
- name_change_arg[c->type - BUS_MATCH_ARG] = c->value_str;
-
- snprintf(buf, sizeof(buf), "arg%u", c->type - BUS_MATCH_ARG);
- bloom_add_pair(bloom, buf, c->value_str);
- using_bloom = true;
- break;
- }
-
- case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST: {
- char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")];
-
- snprintf(buf, sizeof(buf), "arg%u-slash-prefix", c->type - BUS_MATCH_ARG_PATH);
- bloom_add_pair(bloom, buf, c->value_str);
- using_bloom = true;
- break;
- }
-
- case BUS_MATCH_ARG_NAMESPACE...BUS_MATCH_ARG_NAMESPACE_LAST: {
- char buf[sizeof("arg")-1 + 2 + sizeof("-dot-prefix")];
-
- snprintf(buf, sizeof(buf), "arg%u-dot-prefix", c->type - BUS_MATCH_ARG_NAMESPACE);
- bloom_add_pair(bloom, buf, c->value_str);
- using_bloom = true;
- break;
- }
-
- case BUS_MATCH_DESTINATION:
- /* The bloom filter does not include
- the destination, since it is only
- available for broadcast messages
- which do not carry a destination
- since they are undirected. */
- break;
-
- case BUS_MATCH_ROOT:
- case BUS_MATCH_VALUE:
- case BUS_MATCH_LEAF:
- case _BUS_MATCH_NODE_TYPE_MAX:
- case _BUS_MATCH_NODE_TYPE_INVALID:
- assert_not_reached("Invalid match type?");
- }
- }
-
- if (using_bloom)
- sz += ALIGN8(offsetof(struct kdbus_item, data64) + BLOOM_SIZE);
-
- m = alloca0(sz);
- m->size = sz;
- m->cookie = cookie;
- m->owner_id = id;
-
- item = m->items;
-
- if (src_id != KDBUS_MATCH_ID_ANY) {
- item->size = offsetof(struct kdbus_item, id) + sizeof(uint64_t);
- item->type = KDBUS_ITEM_ID;
- item->id = src_id;
- item = KDBUS_ITEM_NEXT(item);
- }
-
- if (using_bloom) {
- item->size = offsetof(struct kdbus_item, data64) + BLOOM_SIZE;
- item->type = KDBUS_ITEM_BLOOM;
- memcpy(item->data64, bloom, BLOOM_SIZE);
- item = KDBUS_ITEM_NEXT(item);
- }
-
- if (sender) {
- item->size = offsetof(struct kdbus_item, str) + sender_length + 1;
- item->type = KDBUS_ITEM_NAME;
- memcpy(item->str, sender, sender_length + 1);
- }
-
- r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
- if (r < 0)
- return -errno;
-
- if (matches_name_change) {
-
- /* If this match could theoretically match
- * NameOwnerChanged messages, we need to
- * install a second non-bloom filter explitly
- * for it */
-
- r = add_name_change_match(bus, cookie, name_change_arg[0], name_change_arg[1], name_change_arg[2]);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int bus_add_match_internal_dbus1(
- sd_bus *bus,
- const char *match) {
-
- assert(bus);
- assert(match);
-
- return sd_bus_call_method(
- bus,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "AddMatch",
- NULL,
- NULL,
- "s",
- match);
-}
-
-int bus_add_match_internal(
- sd_bus *bus,
- const char *match,
- struct bus_match_component *components,
- unsigned n_components,
- uint64_t cookie) {
-
- assert(bus);
- assert(match);
-
- if (bus->is_kernel)
- return bus_add_match_internal_kernel(bus, 0, components, n_components, cookie);
- else
- return bus_add_match_internal_dbus1(bus, match);
-}
-
-int bus_remove_match_internal_kernel(
- sd_bus *bus,
- uint64_t id,
- uint64_t cookie) {
-
- struct kdbus_cmd_match m;
- int r;
-
- assert(bus);
-
- zero(m);
- m.size = offsetof(struct kdbus_cmd_match, items);
- m.cookie = cookie;
- m.owner_id = id;
-
- r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_REMOVE, &m);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-static int bus_remove_match_internal_dbus1(
- sd_bus *bus,
- const char *match) {
-
- assert(bus);
- assert(match);
-
- return sd_bus_call_method(
- bus,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "RemoveMatch",
- NULL,
- NULL,
- "s",
- match);
-}
-
-int bus_remove_match_internal(
- sd_bus *bus,
- const char *match,
- uint64_t cookie) {
-
- assert(bus);
- assert(match);
-
- if (bus->is_kernel)
- return bus_remove_match_internal_kernel(bus, 0, cookie);
- else
- return bus_remove_match_internal_dbus1(bus, match);
-}
-
-_public_ int sd_bus_get_owner_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
- const char *mid;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(name, -EINVAL);
- assert_return(machine, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(service_name_is_valid(name), -EINVAL);
-
- if (streq_ptr(name, bus->unique_name))
- return sd_id128_get_machine(machine);
-
- r = sd_bus_message_new_method_call(
- bus,
- name,
- "/",
- "org.freedesktop.DBus.Peer",
- "GetMachineId", &m);
- if (r < 0)
- return r;
-
- r = sd_bus_message_set_no_auto_start(m, true);
- if (r < 0)
- return r;
-
- r = sd_bus_call(bus, m, 0, NULL, &reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(reply, "s", &mid);
- if (r < 0)
- return r;
-
- return sd_id128_from_string(mid, machine);
-}
diff --git a/src/libsystemd/bus-control.h b/src/libsystemd/bus-control.h
deleted file mode 100644
index b610bef..0000000
--- a/src/libsystemd/bus-control.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "sd-bus.h"
-
-int bus_add_match_internal(sd_bus *bus, const char *match, struct bus_match_component *components, unsigned n_components, uint64_t cookie);
-int bus_remove_match_internal(sd_bus *bus, const char *match, uint64_t cookie);
-
-int bus_add_match_internal_kernel(sd_bus *bus, uint64_t id, struct bus_match_component *components, unsigned n_components, uint64_t cookie);
-int bus_remove_match_internal_kernel(sd_bus *bus, uint64_t id, uint64_t cookie);
diff --git a/src/libsystemd/bus-convenience.c b/src/libsystemd/bus-convenience.c
deleted file mode 100644
index 3964960..0000000
--- a/src/libsystemd/bus-convenience.c
+++ /dev/null
@@ -1,442 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "bus-internal.h"
-#include "bus-message.h"
-#include "bus-signature.h"
-#include "bus-util.h"
-#include "bus-type.h"
-
-_public_ int sd_bus_emit_signal(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *member,
- const char *types, ...) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = sd_bus_message_new_signal(bus, path, interface, member, &m);
- if (r < 0)
- return r;
-
- if (!isempty(types)) {
- va_list ap;
-
- va_start(ap, types);
- r = bus_message_append_ap(m, types, ap);
- va_end(ap);
- if (r < 0)
- return r;
- }
-
- return sd_bus_send(bus, m, NULL);
-}
-
-_public_ int sd_bus_call_method(
- sd_bus *bus,
- const char *destination,
- const char *path,
- const char *interface,
- const char *member,
- sd_bus_error *error,
- sd_bus_message **reply,
- const char *types, ...) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = sd_bus_message_new_method_call(bus, destination, path, interface, member, &m);
- if (r < 0)
- return r;
-
- if (!isempty(types)) {
- va_list ap;
-
- va_start(ap, types);
- r = bus_message_append_ap(m, types, ap);
- va_end(ap);
- if (r < 0)
- return r;
- }
-
- return sd_bus_call(bus, m, 0, error, reply);
-}
-
-_public_ int sd_bus_reply_method_return(
- sd_bus_message *call,
- const char *types, ...) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert_return(call, -EINVAL);
- assert_return(call->sealed, -EPERM);
- assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
- assert_return(call->bus && BUS_IS_OPEN(call->bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(call->bus), -ECHILD);
-
- if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
- return 0;
-
- r = sd_bus_message_new_method_return(call, &m);
- if (r < 0)
- return r;
-
- if (!isempty(types)) {
- va_list ap;
-
- va_start(ap, types);
- r = bus_message_append_ap(m, types, ap);
- va_end(ap);
- if (r < 0)
- return r;
- }
-
- return sd_bus_send(call->bus, m, NULL);
-}
-
-_public_ int sd_bus_reply_method_error(
- sd_bus_message *call,
- const sd_bus_error *e) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert_return(call, -EINVAL);
- assert_return(call->sealed, -EPERM);
- assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
- assert_return(sd_bus_error_is_set(e), -EINVAL);
- assert_return(call->bus && BUS_IS_OPEN(call->bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(call->bus), -ECHILD);
-
- if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
- return 0;
-
- r = sd_bus_message_new_method_error(call, e, &m);
- if (r < 0)
- return r;
-
- return sd_bus_send(call->bus, m, NULL);
-}
-
-_public_ int sd_bus_reply_method_errorf(
- sd_bus_message *call,
- const char *name,
- const char *format,
- ...) {
-
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- va_list ap;
-
- assert_return(call, -EINVAL);
- assert_return(call->sealed, -EPERM);
- assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
- assert_return(call->bus && BUS_IS_OPEN(call->bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(call->bus), -ECHILD);
-
- if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
- return 0;
-
- va_start(ap, format);
- bus_error_setfv(&error, name, format, ap);
- va_end(ap);
-
- return sd_bus_reply_method_error(call, &error);
-}
-
-_public_ int sd_bus_reply_method_errno(
- sd_bus_message *call,
- int error,
- const sd_bus_error *p) {
-
- _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
-
- assert_return(call, -EINVAL);
- assert_return(call->sealed, -EPERM);
- assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
- assert_return(call->bus && BUS_IS_OPEN(call->bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(call->bus), -ECHILD);
-
- if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
- return 0;
-
- if (sd_bus_error_is_set(p))
- return sd_bus_reply_method_error(call, p);
-
- sd_bus_error_set_errno(&berror, error);
-
- return sd_bus_reply_method_error(call, &berror);
-}
-
-_public_ int sd_bus_reply_method_errnof(
- sd_bus_message *call,
- int error,
- const char *format,
- ...) {
-
- _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
- va_list ap;
-
- assert_return(call, -EINVAL);
- assert_return(call->sealed, -EPERM);
- assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
- assert_return(call->bus && BUS_IS_OPEN(call->bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(call->bus), -ECHILD);
-
- if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
- return 0;
-
- va_start(ap, format);
- bus_error_set_errnofv(&berror, error, format, ap);
- va_end(ap);
-
- return sd_bus_reply_method_error(call, &berror);
-}
-
-_public_ int sd_bus_get_property(
- sd_bus *bus,
- const char *destination,
- const char *path,
- const char *interface,
- const char *member,
- sd_bus_error *error,
- sd_bus_message **reply,
- const char *type) {
-
- sd_bus_message *rep = NULL;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
- assert_return(member_name_is_valid(member), -EINVAL);
- assert_return(reply, -EINVAL);
- assert_return(signature_is_single(type, false), -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(rep, 'v', type);
- if (r < 0) {
- sd_bus_message_unref(rep);
- return r;
- }
-
- *reply = rep;
- return 0;
-}
-
-_public_ int sd_bus_get_property_trivial(
- sd_bus *bus,
- const char *destination,
- const char *path,
- const char *interface,
- const char *member,
- sd_bus_error *error,
- char type, void *ptr) {
-
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
- assert_return(member_name_is_valid(member), -EINVAL);
- assert_return(bus_type_is_trivial(type), -EINVAL);
- assert_return(ptr, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type));
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_basic(reply, type, ptr);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-_public_ int sd_bus_get_property_string(
- sd_bus *bus,
- const char *destination,
- const char *path,
- const char *interface,
- const char *member,
- sd_bus_error *error,
- char **ret) {
-
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- const char *s;
- char *n;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
- assert_return(member_name_is_valid(member), -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(reply, 'v', "s");
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_basic(reply, 's', &s);
- if (r < 0)
- return r;
-
- n = strdup(s);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
-}
-
-_public_ int sd_bus_get_property_strv(
- sd_bus *bus,
- const char *destination,
- const char *path,
- const char *interface,
- const char *member,
- sd_bus_error *error,
- char ***ret) {
-
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
- assert_return(member_name_is_valid(member), -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(reply, 'v', NULL);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_strv(reply, ret);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-_public_ int sd_bus_set_property(
- sd_bus *bus,
- const char *destination,
- const char *path,
- const char *interface,
- const char *member,
- sd_bus_error *error,
- const char *type, ...) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- va_list ap;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
- assert_return(member_name_is_valid(member), -EINVAL);
- assert_return(signature_is_single(type, false), -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = sd_bus_message_new_method_call(bus, destination, path, "org.freedesktop.DBus.Properties", "Set", &m);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "ss", strempty(interface), member);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(m, 'v', type);
- if (r < 0)
- return r;
-
- va_start(ap, type);
- r = bus_message_append_ap(m, type, ap);
- va_end(ap);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return r;
-
- return sd_bus_call(bus, m, 0, error, NULL);
-}
-
-_public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) {
- sd_bus_creds *c;
-
- assert_return(call, -EINVAL);
- assert_return(call->sealed, -EPERM);
- assert_return(call->bus && BUS_IS_OPEN(call->bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(call->bus), -ECHILD);
-
- c = sd_bus_message_get_creds(call);
-
- /* All data we need? */
- if (c && (mask & ~c->mask) == 0) {
- *creds = sd_bus_creds_ref(c);
- return 0;
- }
-
- /* No data passed? Or not enough data passed to retrieve the missing bits? */
- if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
- /* We couldn't read anything from the call, let's try
- * to get it from the sender or peer */
-
- if (call->sender)
- return sd_bus_get_owner(call->bus, call->sender, mask, creds);
- else
- return sd_bus_get_peer_creds(call->bus, mask, creds);
- }
-
- return bus_creds_extend_by_pid(c, mask, creds);
-}
diff --git a/src/libsystemd/bus-creds.c b/src/libsystemd/bus-creds.c
deleted file mode 100644
index 52e55fc..0000000
--- a/src/libsystemd/bus-creds.c
+++ /dev/null
@@ -1,904 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdlib.h>
-
-#include "util.h"
-#include "cgroup-util.h"
-#include "fileio.h"
-#include "audit.h"
-#include "bus-message.h"
-#include "bus-util.h"
-#include "time-util.h"
-#include "strv.h"
-#include "bus-creds.h"
-
-enum {
- CAP_OFFSET_INHERITABLE = 0,
- CAP_OFFSET_PERMITTED = 1,
- CAP_OFFSET_EFFECTIVE = 2,
- CAP_OFFSET_BOUNDING = 3
-};
-
-void bus_creds_done(sd_bus_creds *c) {
- assert(c);
-
- /* For internal bus cred structures that are allocated by
- * something else */
-
- free(c->session);
- free(c->unit);
- free(c->user_unit);
- free(c->slice);
-
- strv_free(c->cmdline_array);
- strv_free(c->well_known_names);
-}
-
-_public_ sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c) {
- assert_return(c, NULL);
-
- if (c->allocated) {
- assert(c->n_ref > 0);
- c->n_ref++;
- } else {
- sd_bus_message *m;
-
- /* If this is an embedded creds structure, then
- * forward ref counting to the message */
- m = container_of(c, sd_bus_message, creds);
- sd_bus_message_ref(m);
- }
-
- return c;
-}
-
-_public_ sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c) {
-
- if (!c)
- return NULL;
-
- if (c->allocated) {
- assert(c->n_ref > 0);
- c->n_ref--;
-
- if (c->n_ref == 0) {
- bus_creds_done(c);
-
- free(c->comm);
- free(c->tid_comm);
- free(c->exe);
- free(c->cmdline);
- free(c->cgroup);
- free(c->capability);
- free(c->label);
- free(c->unique_name);
- free(c->cgroup_root);
- free(c);
- }
- } else {
- sd_bus_message *m;
-
- m = container_of(c, sd_bus_message, creds);
- sd_bus_message_unref(m);
- }
-
-
- return NULL;
-}
-
-_public_ uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c) {
- assert_return(c, 0);
-
- return c->mask;
-}
-
-sd_bus_creds* bus_creds_new(void) {
- sd_bus_creds *c;
-
- c = new0(sd_bus_creds, 1);
- if (!c)
- return NULL;
-
- c->allocated = true;
- c->n_ref = 1;
- return c;
-}
-
-_public_ int sd_bus_creds_new_from_pid(pid_t pid, uint64_t mask, sd_bus_creds **ret) {
- sd_bus_creds *c;
- int r;
-
- assert_return(pid >= 0, -EINVAL);
- assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP);
- assert_return(ret, -EINVAL);
-
- if (pid == 0)
- pid = getpid();
-
- c = bus_creds_new();
- if (!c)
- return -ENOMEM;
-
- r = bus_creds_add_more(c, mask, pid, 0);
- if (r < 0) {
- sd_bus_creds_unref(c);
- return r;
- }
-
- /* Check if the process existed at all, in case we haven't
- * figured that out already */
- if (kill(pid, 0) < 0 && errno == ESRCH) {
- sd_bus_creds_unref(c);
- return -ESRCH;
- }
-
- *ret = c;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) {
- assert_return(c, -EINVAL);
- assert_return(uid, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_UID))
- return -ENODATA;
-
- *uid = c->uid;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) {
- assert_return(c, -EINVAL);
- assert_return(gid, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_UID))
- return -ENODATA;
-
- *gid = c->gid;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) {
- assert_return(c, -EINVAL);
- assert_return(pid, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_PID))
- return -ENODATA;
-
- assert(c->pid > 0);
- *pid = c->pid;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid) {
- assert_return(c, -EINVAL);
- assert_return(tid, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_TID))
- return -ENODATA;
-
- assert(c->tid > 0);
- *tid = c->tid;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_pid_starttime(sd_bus_creds *c, uint64_t *usec) {
- assert_return(c, -EINVAL);
- assert_return(usec, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_PID_STARTTIME))
- return -ENODATA;
-
- assert(c->pid_starttime > 0);
- *usec = c->pid_starttime;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **ret) {
- assert_return(c, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_SELINUX_CONTEXT))
- return -ENODATA;
-
- assert(c->label);
- *ret = c->label;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_comm(sd_bus_creds *c, const char **ret) {
- assert_return(c, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_COMM))
- return -ENODATA;
-
- assert(c->comm);
- *ret = c->comm;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_tid_comm(sd_bus_creds *c, const char **ret) {
- assert_return(c, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_TID_COMM))
- return -ENODATA;
-
- assert(c->tid_comm);
- *ret = c->tid_comm;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_exe(sd_bus_creds *c, const char **ret) {
- assert_return(c, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_EXE))
- return -ENODATA;
-
- assert(c->exe);
- *ret = c->exe;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_cgroup(sd_bus_creds *c, const char **ret) {
- assert_return(c, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_CGROUP))
- return -ENODATA;
-
- assert(c->cgroup);
- *ret = c->cgroup;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_unit(sd_bus_creds *c, const char **ret) {
- int r;
-
- assert_return(c, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_UNIT))
- return -ENODATA;
-
- assert(c->cgroup);
-
- if (!c->unit) {
- const char *shifted;
-
- r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
- if (r < 0)
- return r;
-
- r = cg_path_get_unit(shifted, (char**) &c->unit);
- if (r < 0)
- return r;
- }
-
- *ret = c->unit;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **ret) {
- int r;
-
- assert_return(c, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_USER_UNIT))
- return -ENODATA;
-
- assert(c->cgroup);
-
- if (!c->user_unit) {
- const char *shifted;
-
- r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
- if (r < 0)
- return r;
-
- r = cg_path_get_user_unit(shifted, (char**) &c->user_unit);
- if (r < 0)
- return r;
- }
-
- *ret = c->user_unit;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_slice(sd_bus_creds *c, const char **ret) {
- int r;
-
- assert_return(c, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_SLICE))
- return -ENODATA;
-
- assert(c->cgroup);
-
- if (!c->slice) {
- const char *shifted;
-
- r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
- if (r < 0)
- return r;
-
- r = cg_path_get_slice(shifted, (char**) &c->slice);
- if (r < 0)
- return r;
- }
-
- *ret = c->slice;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_session(sd_bus_creds *c, const char **ret) {
- int r;
-
- assert_return(c, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_SESSION))
- return -ENODATA;
-
- assert(c->cgroup);
-
- if (!c->session) {
- const char *shifted;
-
- r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
- if (r < 0)
- return r;
-
- r = cg_path_get_session(shifted, (char**) &c->session);
- if (r < 0)
- return r;
- }
-
- *ret = c->session;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid) {
- const char *shifted;
- int r;
-
- assert_return(c, -EINVAL);
- assert_return(uid, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_OWNER_UID))
- return -ENODATA;
-
- assert(c->cgroup);
-
- r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
- if (r < 0)
- return r;
-
- return cg_path_get_owner_uid(shifted, uid);
-}
-
-_public_ int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline) {
- assert_return(c, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_CMDLINE))
- return -ENODATA;
-
- assert_return(c->cmdline, -ESRCH);
- assert(c->cmdline);
-
- if (!c->cmdline_array) {
- c->cmdline_array = strv_parse_nulstr(c->cmdline, c->cmdline_size);
- if (!c->cmdline_array)
- return -ENOMEM;
- }
-
- *cmdline = c->cmdline_array;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid) {
- assert_return(c, -EINVAL);
- assert_return(sessionid, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_AUDIT_SESSION_ID))
- return -ENODATA;
-
- *sessionid = c->audit_session_id;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *uid) {
- assert_return(c, -EINVAL);
- assert_return(uid, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_AUDIT_LOGIN_UID))
- return -ENODATA;
-
- *uid = c->audit_login_uid;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **unique_name) {
- assert_return(c, -EINVAL);
- assert_return(unique_name, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_UNIQUE_NAME))
- return -ENODATA;
-
- *unique_name = c->unique_name;
- return 0;
-}
-
-_public_ int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***well_known_names) {
- assert_return(c, -EINVAL);
- assert_return(well_known_names, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_WELL_KNOWN_NAMES))
- return -ENODATA;
-
- *well_known_names = c->well_known_names;
- return 0;
-}
-
-static int has_cap(sd_bus_creds *c, unsigned offset, int capability) {
- size_t sz;
-
- assert(c);
- assert(c->capability);
-
- sz = c->capability_size / 4;
- if ((size_t) capability >= sz*8)
- return 0;
-
- return !!(c->capability[offset * sz + (capability / 8)] & (1 << (capability % 8)));
-}
-
-_public_ int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability) {
- assert_return(c, -EINVAL);
- assert_return(capability >= 0, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_EFFECTIVE_CAPS))
- return -ENODATA;
-
- return has_cap(c, CAP_OFFSET_EFFECTIVE, capability);
-}
-
-_public_ int sd_bus_creds_has_permitted_cap(sd_bus_creds *c, int capability) {
- assert_return(c, -EINVAL);
- assert_return(capability >= 0, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_PERMITTED_CAPS))
- return -ENODATA;
-
- return has_cap(c, CAP_OFFSET_PERMITTED, capability);
-}
-
-_public_ int sd_bus_creds_has_inheritable_cap(sd_bus_creds *c, int capability) {
- assert_return(c, -EINVAL);
- assert_return(capability >= 0, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_INHERITABLE_CAPS))
- return -ENODATA;
-
- return has_cap(c, CAP_OFFSET_INHERITABLE, capability);
-}
-
-_public_ int sd_bus_creds_has_bounding_cap(sd_bus_creds *c, int capability) {
- assert_return(c, -EINVAL);
- assert_return(capability >= 0, -EINVAL);
-
- if (!(c->mask & SD_BUS_CREDS_BOUNDING_CAPS))
- return -ENODATA;
-
- return has_cap(c, CAP_OFFSET_BOUNDING, capability);
-}
-
-static int parse_caps(sd_bus_creds *c, unsigned offset, const char *p) {
- size_t sz;
- unsigned i;
-
- assert(c);
- assert(p);
-
- p += strspn(p, WHITESPACE);
-
- sz = strlen(p);
- if (sz % 2 != 0)
- return -EINVAL;
-
- sz /= 2;
- if (!c->capability) {
- c->capability = new0(uint8_t, sz * 4);
- if (!c->capability)
- return -ENOMEM;
-
- c->capability_size = sz * 4;
- }
-
- for (i = 0; i < sz; i ++) {
- int x, y;
-
- x = unhexchar(p[i*2]);
- y = unhexchar(p[i*2+1]);
-
- if (x < 0 || y < 0)
- return -EINVAL;
-
- c->capability[offset * sz + (sz - i - 1)] = (uint8_t) x << 4 | (uint8_t) y;
- }
-
- return 0;
-}
-
-int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
- uint64_t missing;
- int r;
-
- assert(c);
- assert(c->allocated);
-
- missing = mask & ~c->mask;
- if (missing == 0)
- return 0;
-
- /* Try to retrieve PID from creds if it wasn't passed to us */
- if (pid <= 0 && (c->mask & SD_BUS_CREDS_PID))
- pid = c->pid;
-
- if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID))
- tid = c->pid;
-
- /* Without pid we cannot do much... */
- if (pid <= 0)
- return 0;
-
- if (missing & (SD_BUS_CREDS_UID | SD_BUS_CREDS_GID |
- SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_INHERITABLE_CAPS |
- SD_BUS_CREDS_PERMITTED_CAPS | SD_BUS_CREDS_BOUNDING_CAPS)) {
-
- _cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
- const char *p;
-
- p = procfs_file_alloca(pid, "status");
-
- f = fopen(p, "re");
- if (!f)
- return errno == ENOENT ? -ESRCH : -errno;
-
- FOREACH_LINE(line, f, return -errno) {
- truncate_nl(line);
-
- if (missing & SD_BUS_CREDS_UID) {
- p = startswith(line, "Uid:");
- if (p) {
- unsigned long uid;
-
- p += strspn(p, WHITESPACE);
- if (sscanf(p, "%lu", &uid) != 1)
- return -EIO;
-
- c->uid = (uid_t) uid;
- c->mask |= SD_BUS_CREDS_UID;
- continue;
- }
- }
-
- if (missing & SD_BUS_CREDS_GID) {
- p = startswith(line, "Gid:");
- if (p) {
- unsigned long gid;
-
- p += strspn(p, WHITESPACE);
- if (sscanf(p, "%lu", &gid) != 1)
- return -EIO;
-
- c->gid = (uid_t) gid;
- c->mask |= SD_BUS_CREDS_GID;
- continue;
- }
- }
-
- if (missing & SD_BUS_CREDS_EFFECTIVE_CAPS) {
- p = startswith(line, "CapEff:");
- if (p) {
- r = parse_caps(c, CAP_OFFSET_EFFECTIVE, p);
- if (r < 0)
- return r;
-
- c->mask |= SD_BUS_CREDS_EFFECTIVE_CAPS;
- continue;
- }
- }
-
- if (missing & SD_BUS_CREDS_PERMITTED_CAPS) {
- p = startswith(line, "CapPrm:");
- if (p) {
- r = parse_caps(c, CAP_OFFSET_PERMITTED, p);
- if (r < 0)
- return r;
-
- c->mask |= SD_BUS_CREDS_PERMITTED_CAPS;
- continue;
- }
- }
-
- if (missing & SD_BUS_CREDS_INHERITABLE_CAPS) {
- p = startswith(line, "CapInh:");
- if (p) {
- r = parse_caps(c, CAP_OFFSET_INHERITABLE, p);
- if (r < 0)
- return r;
-
- c->mask |= SD_BUS_CREDS_INHERITABLE_CAPS;
- continue;
- }
- }
-
- if (missing & SD_BUS_CREDS_BOUNDING_CAPS) {
- p = startswith(line, "CapBnd:");
- if (p) {
- r = parse_caps(c, CAP_OFFSET_BOUNDING, p);
- if (r < 0)
- return r;
-
- c->mask |= SD_BUS_CREDS_BOUNDING_CAPS;
- continue;
- }
- }
- }
- }
-
- if (missing & (SD_BUS_CREDS_PID_STARTTIME)) {
- unsigned long long st;
-
- r = get_starttime_of_pid(pid, &st);
- if (r < 0)
- return r;
-
- c->pid_starttime = ((usec_t) st * USEC_PER_SEC) / (usec_t) sysconf(_SC_CLK_TCK);
- c->mask |= SD_BUS_CREDS_PID_STARTTIME;
- }
-
- if (missing & SD_BUS_CREDS_SELINUX_CONTEXT) {
- const char *p;
-
- p = procfs_file_alloca(pid, "attr/current");
- r = read_one_line_file(p, &c->label);
- if (r < 0 && r != -ENOENT && r != -EINVAL)
- return r;
- else if (r >= 0)
- c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
- }
-
- if (missing & SD_BUS_CREDS_COMM) {
- r = get_process_comm(pid, &c->comm);
- if (r < 0)
- return r;
-
- c->mask |= SD_BUS_CREDS_COMM;
- }
-
- if (missing & SD_BUS_CREDS_EXE) {
- r = get_process_exe(pid, &c->exe);
- if (r < 0)
- return r;
-
- c->mask |= SD_BUS_CREDS_EXE;
- }
-
- if (missing & SD_BUS_CREDS_CMDLINE) {
- const char *p;
-
- p = procfs_file_alloca(pid, "cmdline");
- r = read_full_file(p, &c->cmdline, &c->cmdline_size);
- if (r < 0)
- return r;
-
- if (c->cmdline_size == 0) {
- free(c->cmdline);
- c->cmdline = NULL;
- } else
- c->mask |= SD_BUS_CREDS_CMDLINE;
- }
-
- if (tid > 0 && (missing & SD_BUS_CREDS_TID_COMM)) {
- _cleanup_free_ char *p = NULL;
-
- if (asprintf(&p, "/proc/%lu/task/%lu/comm", (unsigned long) pid, (unsigned long) tid) < 0)
- return -ENOMEM;
-
- r = read_one_line_file(p, &c->tid_comm);
- if (r < 0)
- return r == -ENOENT ? -ESRCH : r;
-
- c->mask |= SD_BUS_CREDS_TID_COMM;
- }
-
- if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) {
-
- r = cg_pid_get_path(NULL, pid, &c->cgroup);
- if (r < 0)
- return r;
-
- r = cg_get_root_path(&c->cgroup_root);
- if (r < 0)
- return r;
-
- c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID);
- }
-
- if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) {
- r = audit_session_from_pid(pid, &c->audit_session_id);
- if (r < 0 && r != -ENOTSUP && r != -ENXIO && r != -ENOENT)
- return r;
- else if (r >= 0)
- c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID;
- }
-
- if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
- r = audit_loginuid_from_pid(pid, &c->audit_login_uid);
- if (r < 0 && r != -ENOTSUP && r != -ENXIO && r != -ENOENT)
- return r;
- else if (r >= 0)
- c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
- }
-
- return 0;
-}
-
-int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) {
- _cleanup_bus_creds_unref_ sd_bus_creds *n = NULL;
- int r;
-
- assert(c);
- assert(ret);
-
- if ((mask & ~c->mask) == 0) {
- /* There's already all data we need. */
-
- *ret = sd_bus_creds_ref(c);
- return 0;
- }
-
- n = bus_creds_new();
- if (!n)
- return -ENOMEM;
-
- /* Copy the original data over */
-
- if (c->mask & mask & SD_BUS_CREDS_UID) {
- n->uid = c->uid;
- n->mask |= SD_BUS_CREDS_UID;
- }
-
- if (c->mask & mask & SD_BUS_CREDS_GID) {
- n->gid = c->gid;
- n->mask |= SD_BUS_CREDS_GID;
- }
-
- if (c->mask & mask & SD_BUS_CREDS_PID) {
- n->pid = c->pid;
- n->mask |= SD_BUS_CREDS_PID;
- }
-
- if (c->mask & mask & SD_BUS_CREDS_TID) {
- n->tid = c->tid;
- n->mask |= SD_BUS_CREDS_TID;
- }
-
- if (c->mask & mask & SD_BUS_CREDS_PID_STARTTIME) {
- n->pid_starttime = c->pid_starttime;
- n->mask |= SD_BUS_CREDS_PID_STARTTIME;
- }
-
- if (c->mask & mask & SD_BUS_CREDS_COMM) {
- n->comm = strdup(c->comm);
- if (!n->comm)
- return -ENOMEM;
-
- n->mask |= SD_BUS_CREDS_COMM;
- }
-
- if (c->mask & mask & SD_BUS_CREDS_TID_COMM) {
- n->tid_comm = strdup(c->tid_comm);
- if (!n->tid_comm)
- return -ENOMEM;
-
- n->mask |= SD_BUS_CREDS_TID_COMM;
- }
-
- if (c->mask & mask & SD_BUS_CREDS_EXE) {
- n->exe = strdup(c->exe);
- if (!n->exe)
- return -ENOMEM;
-
- n->mask |= SD_BUS_CREDS_EXE;
- }
-
- if (c->mask & mask & SD_BUS_CREDS_CMDLINE) {
- n->cmdline = memdup(c->cmdline, c->cmdline_size);
- if (!n->cmdline)
- return -ENOMEM;
-
- n->cmdline_size = c->cmdline_size;
- n->mask |= SD_BUS_CREDS_CMDLINE;
- }
-
- if (c->mask & mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_OWNER_UID)) {
- n->cgroup = strdup(c->cgroup);
- if (!n->cgroup)
- return -ENOMEM;
-
- n->cgroup_root = strdup(c->cgroup_root);
- if (!n->cgroup_root)
- return -ENOMEM;
-
- n->mask |= mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_OWNER_UID);
- }
-
- if (c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) {
- n->capability = memdup(c->capability, c->capability_size);
- if (!n->capability)
- return -ENOMEM;
-
- n->capability_size = c->capability_size;
- n->mask |= c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS);
- }
-
- if (c->mask & mask & SD_BUS_CREDS_AUDIT_SESSION_ID) {
- n->audit_session_id = c->audit_session_id;
- n->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID;
- }
-
- if (c->mask & mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
- n->audit_login_uid = c->audit_login_uid;
- n->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
- }
-
- if (c->mask & mask & SD_BUS_CREDS_UNIQUE_NAME) {
- n->unique_name = strdup(c->unique_name);
- if (!n->unique_name)
- return -ENOMEM;
- }
-
- if (c->mask & mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
- n->well_known_names = strv_copy(c->well_known_names);
- if (!n->well_known_names)
- return -ENOMEM;
- }
-
- /* Get more data */
-
- r = bus_creds_add_more(n, mask,
- c->mask & SD_BUS_CREDS_PID ? c->pid : 0,
- c->mask & SD_BUS_CREDS_TID ? c->tid : 0);
- if (r < 0)
- return r;
-
- *ret = n;
- n = NULL;
- return 0;
-}
diff --git a/src/libsystemd/bus-creds.h b/src/libsystemd/bus-creds.h
deleted file mode 100644
index d8b4aca..0000000
--- a/src/libsystemd/bus-creds.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-
-#include "sd-bus.h"
-#include "time-util.h"
-
-struct sd_bus_creds {
- bool allocated;
- unsigned n_ref;
- uint64_t mask;
-
- uid_t uid;
- gid_t gid;
- pid_t pid;
- usec_t pid_starttime;
- pid_t tid;
-
- char *comm;
- char *tid_comm;
- char *exe;
-
- char *cmdline;
- size_t cmdline_size;
- char **cmdline_array;
-
- char *cgroup;
- char *session;
- char *unit;
- char *user_unit;
- char *slice;
-
- uint8_t *capability;
- size_t capability_size;
-
- uint32_t audit_session_id;
- uid_t audit_login_uid;
-
- char *label;
-
- char *unique_name;
-
- char **well_known_names;
-
- char *cgroup_root;
-};
-
-sd_bus_creds* bus_creds_new(void);
-
-void bus_creds_done(sd_bus_creds *c);
-
-int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid);
-
-int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret);
diff --git a/src/libsystemd/bus-dump.c b/src/libsystemd/bus-dump.c
deleted file mode 100644
index 78e7597..0000000
--- a/src/libsystemd/bus-dump.c
+++ /dev/null
@@ -1,417 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-#include "capability.h"
-#include "strv.h"
-#include "audit.h"
-
-#include "bus-message.h"
-#include "bus-internal.h"
-#include "bus-type.h"
-#include "bus-dump.h"
-
-static char *indent(unsigned level) {
- char *p;
-
- p = new(char, 2 + level + 1);
- if (!p)
- return NULL;
-
- p[0] = p[1] = ' ';
- memset(p + 2, '\t', level);
- p[2 + level] = 0;
-
- return p;
-}
-
-int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) {
- unsigned level = 1;
- int r;
-
- assert(m);
-
- if (!f)
- f = stdout;
-
- if (with_header) {
- fprintf(f,
- "%s%s%sType=%s%s%s Endian=%c Flags=%u Version=%u",
- m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() :
- m->header->type == SD_BUS_MESSAGE_METHOD_RETURN ? ansi_highlight_green() :
- m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", draw_special_char(DRAW_TRIANGULAR_BULLET), ansi_highlight_off(),
- ansi_highlight(), bus_message_type_to_string(m->header->type), ansi_highlight_off(),
- m->header->endian,
- m->header->flags,
- m->header->version);
-
- /* Display synthetic message serial number in a more readable
- * format than (uint32_t) -1 */
- if (BUS_MESSAGE_COOKIE(m) == 0xFFFFFFFFULL)
- fprintf(f, " Cookie=-1");
- else
- fprintf(f, " Cookie=%lu", (unsigned long) BUS_MESSAGE_COOKIE(m));
-
- if (m->reply_cookie != 0)
- fprintf(f, " ReplyCookie=%lu", (unsigned long) m->reply_cookie);
-
- fputs("\n", f);
-
- if (m->sender)
- fprintf(f, " Sender=%s%s%s", ansi_highlight(), m->sender, ansi_highlight_off());
- if (m->destination)
- fprintf(f, " Destination=%s%s%s", ansi_highlight(), m->destination, ansi_highlight_off());
- if (m->path)
- fprintf(f, " Path=%s%s%s", ansi_highlight(), m->path, ansi_highlight_off());
- if (m->interface)
- fprintf(f, " Interface=%s%s%s", ansi_highlight(), m->interface, ansi_highlight_off());
- if (m->member)
- fprintf(f, " Member=%s%s%s", ansi_highlight(), m->member, ansi_highlight_off());
-
- if (m->sender || m->destination || m->path || m->interface || m->member)
- fputs("\n", f);
-
- if (sd_bus_error_is_set(&m->error))
- fprintf(f,
- " ErrorName=%s%s%s"
- " ErrorMessage=%s\"%s\"%s\n",
- ansi_highlight_red(), strna(m->error.name), ansi_highlight_off(),
- ansi_highlight_red(), strna(m->error.message), ansi_highlight_off());
-
- if (m->monotonic != 0)
- fprintf(f, " Monotonic=%llu", (unsigned long long) m->monotonic);
- if (m->realtime != 0)
- fprintf(f, " Realtime=%llu", (unsigned long long) m->realtime);
-
- if (m->monotonic != 0 || m->realtime != 0)
- fputs("\n", f);
-
- bus_creds_dump(&m->creds, f);
- }
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0) {
- log_error("Failed to rewind: %s", strerror(-r));
- return r;
- }
-
- fprintf(f, " MESSAGE \"%s\" {\n", strempty(m->root_container.signature));
-
- for (;;) {
- _cleanup_free_ char *prefix = NULL;
- const char *contents = NULL;
- char type;
- union {
- uint8_t u8;
- uint16_t u16;
- int16_t s16;
- uint32_t u32;
- int32_t s32;
- uint64_t u64;
- int64_t s64;
- double d64;
- const char *string;
- int i;
- } basic;
-
- r = sd_bus_message_peek_type(m, &type, &contents);
- if (r < 0) {
- log_error("Failed to peek type: %s", strerror(-r));
- return r;
- }
-
- if (r == 0) {
- if (level <= 1)
- break;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0) {
- log_error("Failed to exit container: %s", strerror(-r));
- return r;
- }
-
- level--;
-
- prefix = indent(level);
- if (!prefix)
- return log_oom();
-
- fprintf(f, "%s};\n", prefix);
- continue;
- }
-
- prefix = indent(level);
- if (!prefix)
- return log_oom();
-
- if (bus_type_is_container(type) > 0) {
- r = sd_bus_message_enter_container(m, type, contents);
- if (r < 0) {
- log_error("Failed to enter container: %s", strerror(-r));
- return r;
- }
-
- if (type == SD_BUS_TYPE_ARRAY)
- fprintf(f, "%sARRAY \"%s\" {\n", prefix, contents);
- else if (type == SD_BUS_TYPE_VARIANT)
- fprintf(f, "%sVARIANT \"%s\" {\n", prefix, contents);
- else if (type == SD_BUS_TYPE_STRUCT)
- fprintf(f, "%sSTRUCT \"%s\" {\n", prefix, contents);
- else if (type == SD_BUS_TYPE_DICT_ENTRY)
- fprintf(f, "%sDICT_ENTRY \"%s\" {\n", prefix, contents);
-
- level ++;
-
- continue;
- }
-
- r = sd_bus_message_read_basic(m, type, &basic);
- if (r < 0) {
- log_error("Failed to get basic: %s", strerror(-r));
- return r;
- }
-
- assert(r > 0);
-
- switch (type) {
-
- case SD_BUS_TYPE_BYTE:
- fprintf(f, "%sBYTE %s%u%s;\n", prefix, ansi_highlight(), basic.u8, ansi_highlight_off());
- break;
-
- case SD_BUS_TYPE_BOOLEAN:
- fprintf(f, "%sBOOLEAN %s%s%s;\n", prefix, ansi_highlight(), true_false(basic.i), ansi_highlight_off());
- break;
-
- case SD_BUS_TYPE_INT16:
- fprintf(f, "%sINT16 %s%i%s;\n", prefix, ansi_highlight(), basic.s16, ansi_highlight_off());
- break;
-
- case SD_BUS_TYPE_UINT16:
- fprintf(f, "%sUINT16 %s%u%s;\n", prefix, ansi_highlight(), basic.u16, ansi_highlight_off());
- break;
-
- case SD_BUS_TYPE_INT32:
- fprintf(f, "%sINT32 %s%i%s;\n", prefix, ansi_highlight(), basic.s32, ansi_highlight_off());
- break;
-
- case SD_BUS_TYPE_UINT32:
- fprintf(f, "%sUINT32 %s%u%s;\n", prefix, ansi_highlight(), basic.u32, ansi_highlight_off());
- break;
-
- case SD_BUS_TYPE_INT64:
- fprintf(f, "%sINT64 %s%lli%s;\n", prefix, ansi_highlight(), (long long) basic.s64, ansi_highlight_off());
- break;
-
- case SD_BUS_TYPE_UINT64:
- fprintf(f, "%sUINT64 %s%llu%s;\n", prefix, ansi_highlight(), (unsigned long long) basic.u64, ansi_highlight_off());
- break;
-
- case SD_BUS_TYPE_DOUBLE:
- fprintf(f, "%sDOUBLE %s%g%s;\n", prefix, ansi_highlight(), basic.d64, ansi_highlight_off());
- break;
-
- case SD_BUS_TYPE_STRING:
- fprintf(f, "%sSTRING \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_highlight_off());
- break;
-
- case SD_BUS_TYPE_OBJECT_PATH:
- fprintf(f, "%sOBJECT_PATH \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_highlight_off());
- break;
-
- case SD_BUS_TYPE_SIGNATURE:
- fprintf(f, "%sSIGNATURE \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_highlight_off());
- break;
-
- case SD_BUS_TYPE_UNIX_FD:
- fprintf(f, "%sUNIX_FD %s%i%s;\n", prefix, ansi_highlight(), basic.i, ansi_highlight_off());
- break;
-
- default:
- assert_not_reached("Unknown basic type.");
- }
- }
-
- fprintf(f, " };\n\n");
- return 0;
-}
-
-static void dump_capabilities(
- sd_bus_creds *c,
- FILE *f,
- const char *name,
- int (*has)(sd_bus_creds *c, int capability)) {
-
- unsigned long i, last_cap;
- unsigned n = 0;
- int r;
-
- assert(c);
- assert(f);
- assert(name);
- assert(has);
-
- i = 0;
- r = has(c, i);
- if (r < 0)
- return;
-
- fprintf(f, " %s=", name);
- last_cap = cap_last_cap();
-
- for (;;) {
- if (r > 0) {
- _cleanup_cap_free_charp_ char *t;
-
- if (n > 0)
- fputc(' ', f);
- if (n % 4 == 3)
- fputs("\n ", f);
-
- t = cap_to_name(i);
- fprintf(f, "%s", t);
- n++;
- }
-
- i++;
-
- if (i > last_cap)
- break;
-
- r = has(c, i);
- }
-
- fputs("\n", f);
-}
-
-int bus_creds_dump(sd_bus_creds *c, FILE *f) {
- bool audit_sessionid_is_set = false, audit_loginuid_is_set = false;
- const char *u = NULL, *uu = NULL, *s = NULL, *sl = NULL;
- uid_t owner, audit_loginuid;
- uint32_t audit_sessionid;
- char **cmdline = NULL, **well_known = NULL;
- int r;
-
- assert(c);
-
- if (!f)
- f = stdout;
-
- if (c->mask & SD_BUS_CREDS_PID)
- fprintf(f, " PID=%lu", (unsigned long) c->pid);
- if (c->mask & SD_BUS_CREDS_PID_STARTTIME)
- fprintf(f, " PIDStartTime=%llu", (unsigned long long) c->pid_starttime);
- if (c->mask & SD_BUS_CREDS_TID)
- fprintf(f, " TID=%lu", (unsigned long) c->tid);
- if (c->mask & SD_BUS_CREDS_UID)
- fprintf(f, " UID=%lu", (unsigned long) c->uid);
- r = sd_bus_creds_get_owner_uid(c, &owner);
- if (r >= 0)
- fprintf(f, " OwnerUID=%lu", (unsigned long) owner);
- if (c->mask & SD_BUS_CREDS_GID)
- fprintf(f, " GID=%lu", (unsigned long) c->gid);
-
- if ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID|SD_BUS_CREDS_UID|SD_BUS_CREDS_GID)) || r >= 0)
- fputs("\n", f);
-
- if (c->mask & SD_BUS_CREDS_EXE)
- fprintf(f, " Exe=%s", c->exe);
- if (c->mask & SD_BUS_CREDS_COMM)
- fprintf(f, " Comm=%s", c->comm);
- if (c->mask & SD_BUS_CREDS_TID_COMM)
- fprintf(f, " TIDComm=%s", c->tid_comm);
- if (c->mask & SD_BUS_CREDS_SELINUX_CONTEXT)
- fprintf(f, " Label=%s", c->label);
-
- if (c->mask & (SD_BUS_CREDS_EXE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_SELINUX_CONTEXT))
- fputs("\n", f);
-
- if (sd_bus_creds_get_cmdline(c, &cmdline) >= 0) {
- char **i;
-
- fputs(" CommandLine={", f);
- STRV_FOREACH(i, cmdline) {
- if (i != cmdline)
- fputc(' ', f);
-
- fputs(*i, f);
- }
-
- fputs("}\n", f);
- }
-
- if (c->mask & SD_BUS_CREDS_CGROUP)
- fprintf(f, " CGroup=%s", c->cgroup);
- sd_bus_creds_get_unit(c, &u);
- if (u)
- fprintf(f, " Unit=%s", u);
- sd_bus_creds_get_user_unit(c, &uu);
- if (uu)
- fprintf(f, " UserUnit=%s", uu);
- sd_bus_creds_get_slice(c, &sl);
- if (sl)
- fprintf(f, " Slice=%s", sl);
- sd_bus_creds_get_session(c, &s);
- if (s)
- fprintf(f, " Session=%s", s);
-
- if ((c->mask & SD_BUS_CREDS_CGROUP) || u || uu || sl || s)
- fputs("\n", f);
-
- if (sd_bus_creds_get_audit_login_uid(c, &audit_loginuid) >= 0) {
- audit_loginuid_is_set = true;
- fprintf(f, " AuditLoginUID=%lu", (unsigned long) audit_loginuid);
- }
- if (sd_bus_creds_get_audit_session_id(c, &audit_sessionid) >= 0) {
- audit_sessionid_is_set = true;
- fprintf(f, " AuditSessionID=%lu", (unsigned long) audit_sessionid);
- }
-
- if (audit_loginuid_is_set || audit_sessionid_is_set)
- fputs("\n", f);
-
- if (c->mask & SD_BUS_CREDS_UNIQUE_NAME)
- fprintf(f, " UniqueName=%s", c->unique_name);
-
- if (sd_bus_creds_get_well_known_names(c, &well_known) >= 0) {
- char **i;
-
- fputs(" WellKnownNames={", f);
- STRV_FOREACH(i, well_known) {
- if (i != well_known)
- fputc(' ', f);
-
- fputs(*i, f);
- }
-
- fputc('}', f);
- }
-
- if (c->mask & SD_BUS_CREDS_UNIQUE_NAME || well_known)
- fputc('\n', f);
-
- dump_capabilities(c, f, "EffectiveCapabilities", sd_bus_creds_has_effective_cap);
- dump_capabilities(c, f, "PermittedCapabilities", sd_bus_creds_has_permitted_cap);
- dump_capabilities(c, f, "InheritableCapabilities", sd_bus_creds_has_inheritable_cap);
- dump_capabilities(c, f, "BoundingCapabilities", sd_bus_creds_has_bounding_cap);
-
- return 0;
-}
diff --git a/src/libsystemd/bus-dump.h b/src/libsystemd/bus-dump.h
deleted file mode 100644
index bb1d25d..0000000
--- a/src/libsystemd/bus-dump.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdio.h>
-#include <stdbool.h>
-
-#include "sd-bus.h"
-
-int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header);
-
-int bus_creds_dump(sd_bus_creds *c, FILE *f);
diff --git a/src/libsystemd/bus-error-mapping.gperf b/src/libsystemd/bus-error-mapping.gperf
deleted file mode 100644
index df2c4d4..0000000
--- a/src/libsystemd/bus-error-mapping.gperf
+++ /dev/null
@@ -1,49 +0,0 @@
-%{
-#include <errno.h>
-#include "bus-error.h"
-%}
-name_error_mapping;
-%null_strings
-%language=ANSI-C
-%define slot-name name
-%define hash-function-name bus_error_mapping_hash
-%define lookup-function-name bus_error_mapping_lookup
-%readonly-tables
-%omit-struct-type
-%struct-type
-%includes
-%%
-org.freedesktop.DBus.Error.Failed, EACCES
-org.freedesktop.DBus.Error.NoMemory, ENOMEM
-org.freedesktop.DBus.Error.ServiceUnknown, EHOSTUNREACH
-org.freedesktop.DBus.Error.NameHasNoOwner, ENXIO
-org.freedesktop.DBus.Error.NoReply, ETIMEDOUT
-org.freedesktop.DBus.Error.IOError, EIO
-org.freedesktop.DBus.Error.BadAddress, EADDRNOTAVAIL
-org.freedesktop.DBus.Error.NotSupported, ENOTSUP
-org.freedesktop.DBus.Error.LimitsExceeded, ENOBUFS
-org.freedesktop.DBus.Error.AccessDenied, EACCES
-org.freedesktop.DBus.Error.AuthFailed, EACCES
-org.freedesktop.DBus.Error.NoServer, EHOSTDOWN
-org.freedesktop.DBus.Error.Timeout, ETIMEDOUT
-org.freedesktop.DBus.Error.NoNetwork, ENONET
-org.freedesktop.DBus.Error.AddressInUse, EADDRINUSE
-org.freedesktop.DBus.Error.Disconnected, ECONNRESET
-org.freedesktop.DBus.Error.InvalidArgs, EINVAL
-org.freedesktop.DBus.Error.FileNotFound, ENOENT
-org.freedesktop.DBus.Error.FileExists, EEXIST
-org.freedesktop.DBus.Error.UnknownMethod, EBADR
-org.freedesktop.DBus.Error.UnknownObject, EBADR
-org.freedesktop.DBus.Error.UnknownInterface, EBADR
-org.freedesktop.DBus.Error.UnknownProperty, EBADR
-org.freedesktop.DBus.Error.PropertyReadOnly, EROFS
-org.freedesktop.DBus.Error.UnixProcessIdUnknown, ESRCH
-org.freedesktop.DBus.Error.InvalidSignature, EINVAL
-org.freedesktop.DBus.Error.InconsistentMessage, EBADMSG
-#
-org.freedesktop.DBus.Error.TimedOut, ETIMEDOUT
-org.freedesktop.DBus.Error.MatchRuleInvalid, EINVAL
-org.freedesktop.DBus.Error.InvalidFileContent, EINVAL
-org.freedesktop.DBus.Error.MatchRuleNotFound, ENOENT
-org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown, ESRCH
-org.freedesktop.DBus.Error.ObjectPathInUse, EBUSY
diff --git a/src/libsystemd/bus-error.c b/src/libsystemd/bus-error.c
deleted file mode 100644
index c2e41fb..0000000
--- a/src/libsystemd/bus-error.c
+++ /dev/null
@@ -1,504 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "util.h"
-#include "errno-list.h"
-
-#include "sd-bus.h"
-#include "bus-error.h"
-
-#define BUS_ERROR_OOM SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_MEMORY, "Out of memory")
-#define BUS_ERROR_FAILED SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FAILED, "Operation failed")
-
-static int bus_error_name_to_errno(const char *name) {
- const char *p;
- int r;
- const name_error_mapping *m;
-
- if (!name)
- return EINVAL;
-
- p = startswith(name, "System.Error.");
- if (p) {
- r = errno_from_name(p);
- if (r <= 0)
- return EIO;
-
- return r;
- }
-
- m = bus_error_mapping_lookup(name, strlen(name));
- if (m)
- return m->code;
-
- return EIO;
-}
-
-static sd_bus_error errno_to_bus_error_const(int error) {
-
- if (error < 0)
- error = -error;
-
- switch (error) {
-
- case ENOMEM:
- return BUS_ERROR_OOM;
-
- case EPERM:
- case EACCES:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied");
-
- case EINVAL:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument");
-
- case ESRCH:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process");
-
- case ENOENT:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found");
-
- case EEXIST:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists");
-
- case ETIMEDOUT:
- case ETIME:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out");
-
- case EIO:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error");
-
- case ENETRESET:
- case ECONNABORTED:
- case ECONNRESET:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected");
-
- case ENOTSUP:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported");
-
- case EADDRNOTAVAIL:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available");
-
- case ENOBUFS:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded");
-
- case EADDRINUSE:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use");
-
- case EBADMSG:
- return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message");
- }
-
- return SD_BUS_ERROR_NULL;
-}
-
-static int errno_to_bus_error_name_new(int error, char **ret) {
- const char *name;
- char *n;
-
- if (error < 0)
- error = -error;
-
- name = errno_to_name(error);
- if (!name)
- return 0;
-
- n = strappend("System.Error.", name);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 1;
-}
-
-bool bus_error_is_dirty(sd_bus_error *e) {
- if (!e)
- return false;
-
- return e->name || e->message || e->_need_free != 0;
-}
-
-_public_ void sd_bus_error_free(sd_bus_error *e) {
- if (!e)
- return;
-
- if (e->_need_free > 0) {
- free((void*) e->name);
- free((void*) e->message);
- }
-
- e->name = e->message = NULL;
- e->_need_free = 0;
-}
-
-_public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
-
- if (!name)
- return 0;
- if (!e)
- goto finish;
-
- assert_return(!bus_error_is_dirty(e), -EINVAL);
-
- e->name = strdup(name);
- if (!e->name) {
- *e = BUS_ERROR_OOM;
- return -ENOMEM;
- }
-
- if (message)
- e->message = strdup(message);
-
- e->_need_free = 1;
-
-finish:
- return -bus_error_name_to_errno(name);
-}
-
-int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
-
- if (!name)
- return 0;
- if (!e)
- goto finish;
-
- assert_return(!bus_error_is_dirty(e), -EINVAL);
-
- e->name = strdup(name);
- if (!e->name) {
- *e = BUS_ERROR_OOM;
- return -ENOMEM;
- }
-
- if (format)
- vasprintf((char**) &e->message, format, ap);
-
- e->_need_free = 1;
-
-finish:
- return -bus_error_name_to_errno(name);
-}
-
-_public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
-
- if (format) {
- int r;
- va_list ap;
-
- va_start(ap, format);
- r = bus_error_setfv(e, name, format, ap);
- va_end(ap);
-
- return r;
- }
-
- return sd_bus_error_set(e, name, NULL);
-}
-
-_public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
-
- if (!sd_bus_error_is_set(e))
- return 0;
- if (!dest)
- goto finish;
-
- assert_return(!bus_error_is_dirty(dest), -EINVAL);
-
- /*
- * _need_free < 0 indicates that the error is temporarily const, needs deep copying
- * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
- * _need_free > 0 indicates that the error is fully dynamic, needs deep copying
- */
-
- if (e->_need_free == 0)
- *dest = *e;
- else {
- dest->name = strdup(e->name);
- if (!dest->name) {
- *dest = BUS_ERROR_OOM;
- return -ENOMEM;
- }
-
- if (e->message)
- dest->message = strdup(e->message);
-
- dest->_need_free = 1;
- }
-
-finish:
- return -bus_error_name_to_errno(e->name);
-}
-
-_public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
- if (!name)
- return 0;
- if (!e)
- goto finish;
-
- assert_return(!bus_error_is_dirty(e), -EINVAL);
-
- *e = SD_BUS_ERROR_MAKE_CONST(name, message);
-
-finish:
- return -bus_error_name_to_errno(name);
-}
-
-_public_ int sd_bus_error_is_set(const sd_bus_error *e) {
- if (!e)
- return 0;
-
- return !!e->name;
-}
-
-_public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
- if (!e)
- return 0;
-
- return streq_ptr(e->name, name);
-}
-
-_public_ int sd_bus_error_get_errno(const sd_bus_error* e) {
- if (!e)
- return 0;
-
- if (!e->name)
- return 0;
-
- return bus_error_name_to_errno(e->name);
-}
-
-static void bus_error_strerror(sd_bus_error *e, int error) {
- size_t k = 64;
- char *m;
-
- assert(e);
-
- for (;;) {
- char *x;
-
- m = new(char, k);
- if (!m)
- return;
-
- errno = 0;
- x = strerror_r(error, m, k);
- if (errno == ERANGE || strlen(x) >= k - 1) {
- free(m);
- k *= 2;
- continue;
- }
-
- if (!x || errno) {
- free(m);
- return;
- }
-
- if (x == m) {
- if (e->_need_free > 0) {
- /* Error is already dynamic, let's just update the message */
- free((char*) e->message);
- e->message = x;
-
- } else {
- char *t;
- /* Error was const so far, let's make it dynamic, if we can */
-
- t = strdup(e->name);
- if (!t) {
- free(m);
- return;
- }
-
- e->_need_free = 1;
- e->name = t;
- e->message = x;
- }
- } else {
- free(m);
-
- if (e->_need_free > 0) {
- char *t;
-
- /* Error is dynamic, let's hence make the message also dynamic */
- t = strdup(x);
- if (!t)
- return;
-
- free((char*) e->message);
- e->message = t;
- } else {
- /* Error is const, hence we can just override */
- e->message = x;
- }
- }
-
- return;
- }
-}
-
-_public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
-
- if (error < 0)
- error = -error;
-
- if (!e)
- return -error;
- if (error == 0)
- return -error;
-
- assert_return(!bus_error_is_dirty(e), -EINVAL);
-
- /* First, try a const translation */
- *e = errno_to_bus_error_const(error);
-
- if (!sd_bus_error_is_set(e)) {
- int k;
-
- /* If that didn't work, try a dynamic one. */
-
- k = errno_to_bus_error_name_new(error, (char**) &e->name);
- if (k > 0)
- e->_need_free = 1;
- else if (k < 0) {
- *e = BUS_ERROR_OOM;
- return -error;
- } else
- *e = BUS_ERROR_FAILED;
- }
-
- /* Now, fill in the message from strerror() if we can */
- bus_error_strerror(e, error);
- return -error;
-}
-
-int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
- int r;
-
- if (error < 0)
- error = -error;
-
- if (!e)
- return -error;
- if (error == 0)
- return 0;
-
- assert_return(!bus_error_is_dirty(e), -EINVAL);
-
- /* First, try a const translation */
- *e = errno_to_bus_error_const(error);
-
- if (!sd_bus_error_is_set(e)) {
- int k;
-
- /* If that didn't work, try a dynamic one */
-
- k = errno_to_bus_error_name_new(error, (char**) &e->name);
- if (k > 0)
- e->_need_free = 1;
- else if (k < 0) {
- *e = BUS_ERROR_OOM;
- return -ENOMEM;
- } else
- *e = BUS_ERROR_FAILED;
- }
-
- if (format) {
- char *m;
-
- /* First, let's try to fill in the supplied message */
-
- r = vasprintf(&m, format, ap);
- if (r >= 0) {
-
- if (e->_need_free <= 0) {
- char *t;
-
- t = strdup(e->name);
- if (t) {
- e->_need_free = 1;
- e->name = t;
- e->message = m;
- return -error;
- }
-
- free(m);
- } else {
- free((char*) e->message);
- e->message = m;
- return -error;
- }
- }
- }
-
- /* If that didn't work, use strerror() for the message */
- bus_error_strerror(e, error);
- return -error;
-}
-
-_public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
- int r;
-
- if (error < 0)
- error = -error;
-
- if (!e)
- return -error;
- if (error == 0)
- return 0;
-
- assert_return(!bus_error_is_dirty(e), -EINVAL);
-
- if (format) {
- va_list ap;
-
- va_start(ap, format);
- r = bus_error_set_errnofv(e, error, format, ap);
- va_end(ap);
-
- return r;
- }
-
- return sd_bus_error_set_errno(e, error);
-}
-
-const char *bus_error_message(const sd_bus_error *e, int error) {
-
- if (e) {
- /* Sometimes the D-Bus server is a little bit too verbose with
- * its error messages, so let's override them here */
- if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
- return "Access denied";
-
- if (e->message)
- return e->message;
- }
-
- if (error < 0)
- error = -error;
-
- return strerror(error);
-}
diff --git a/src/libsystemd/bus-error.h b/src/libsystemd/bus-error.h
deleted file mode 100644
index cf0ad9d..0000000
--- a/src/libsystemd/bus-error.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-
-#include "sd-bus.h"
-#include "macro.h"
-
-struct name_error_mapping {
- const char* name;
- int code;
-};
-typedef struct name_error_mapping name_error_mapping;
-
-const name_error_mapping* bus_error_mapping_lookup(const char *str, unsigned int len);
-
-bool bus_error_is_dirty(sd_bus_error *e);
-
-const char *bus_error_message(const sd_bus_error *e, int error);
-
-int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) _printf_(3,0);
-int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _printf_(3,0);
diff --git a/src/libsystemd/bus-gvariant.c b/src/libsystemd/bus-gvariant.c
deleted file mode 100644
index dc40009..0000000
--- a/src/libsystemd/bus-gvariant.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-#include "bus-type.h"
-#include "bus-gvariant.h"
-#include "bus-signature.h"
-
-int bus_gvariant_get_size(const char *signature) {
- const char *p;
- int sum = 0, r;
-
- /* For fixed size structs. Fails for variable size structs. */
-
- p = signature;
- while (*p != 0) {
- size_t n;
-
- r = signature_element_length(p, &n);
- if (r < 0)
- return r;
- else {
- char t[n+1];
-
- memcpy(t, p, n);
- t[n] = 0;
-
- r = bus_gvariant_get_alignment(t);
- if (r < 0)
- return r;
-
- sum = ALIGN_TO(sum, r);
- }
-
- switch (*p) {
-
- case SD_BUS_TYPE_BOOLEAN:
- case SD_BUS_TYPE_BYTE:
- sum += 1;
- break;
-
- case SD_BUS_TYPE_INT16:
- case SD_BUS_TYPE_UINT16:
- sum += 2;
- break;
-
- case SD_BUS_TYPE_INT32:
- case SD_BUS_TYPE_UINT32:
- case SD_BUS_TYPE_UNIX_FD:
- sum += 4;
- break;
-
- case SD_BUS_TYPE_INT64:
- case SD_BUS_TYPE_UINT64:
- case SD_BUS_TYPE_DOUBLE:
- sum += 8;
- break;
-
- case SD_BUS_TYPE_STRUCT_BEGIN:
- case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
- char t[n-1];
-
- memcpy(t, p + 1, n - 2);
- t[n - 2] = 0;
-
- r = bus_gvariant_get_size(t);
- if (r < 0)
- return r;
-
- sum += r;
- break;
- }
-
- case SD_BUS_TYPE_STRING:
- case SD_BUS_TYPE_OBJECT_PATH:
- case SD_BUS_TYPE_SIGNATURE:
- case SD_BUS_TYPE_ARRAY:
- case SD_BUS_TYPE_VARIANT:
- return -EINVAL;
-
- default:
- assert_not_reached("Unknown signature type");
- }
-
- p += n;
- }
-
- r = bus_gvariant_get_alignment(signature);
- if (r < 0)
- return r;
-
- return ALIGN_TO(sum, r);
-}
-
-int bus_gvariant_get_alignment(const char *signature) {
- size_t alignment = 1;
- const char *p;
- int r;
-
- p = signature;
- while (*p != 0 && alignment < 8) {
- size_t n;
- int a;
-
- r = signature_element_length(p, &n);
- if (r < 0)
- return r;
-
- switch (*p) {
-
- case SD_BUS_TYPE_BYTE:
- case SD_BUS_TYPE_BOOLEAN:
- case SD_BUS_TYPE_STRING:
- case SD_BUS_TYPE_OBJECT_PATH:
- case SD_BUS_TYPE_SIGNATURE:
- a = 1;
- break;
-
- case SD_BUS_TYPE_INT16:
- case SD_BUS_TYPE_UINT16:
- a = 2;
- break;
-
- case SD_BUS_TYPE_INT32:
- case SD_BUS_TYPE_UINT32:
- case SD_BUS_TYPE_UNIX_FD:
- a = 4;
- break;
-
- case SD_BUS_TYPE_INT64:
- case SD_BUS_TYPE_UINT64:
- case SD_BUS_TYPE_DOUBLE:
- case SD_BUS_TYPE_VARIANT:
- a = 8;
- break;
-
- case SD_BUS_TYPE_ARRAY: {
- char t[n];
-
- memcpy(t, p + 1, n - 1);
- t[n - 1] = 0;
-
- a = bus_gvariant_get_alignment(t);
- break;
- }
-
- case SD_BUS_TYPE_STRUCT_BEGIN:
- case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
- char t[n-1];
-
- memcpy(t, p + 1, n - 2);
- t[n - 2] = 0;
-
- a = bus_gvariant_get_alignment(t);
- break;
- }
-
- default:
- assert_not_reached("Unknown signature type");
- }
-
- if (a < 0)
- return a;
-
- assert(a > 0 && a <= 8);
- if ((size_t) a > alignment)
- alignment = (size_t) a;
-
- p += n;
- }
-
- return alignment;
-}
-
-int bus_gvariant_is_fixed_size(const char *signature) {
- const char *p;
- int r;
-
- assert(signature);
-
- p = signature;
- while (*p != 0) {
- size_t n;
-
- r = signature_element_length(p, &n);
- if (r < 0)
- return r;
-
- switch (*p) {
-
- case SD_BUS_TYPE_STRING:
- case SD_BUS_TYPE_OBJECT_PATH:
- case SD_BUS_TYPE_SIGNATURE:
- case SD_BUS_TYPE_ARRAY:
- case SD_BUS_TYPE_VARIANT:
- return 0;
-
- case SD_BUS_TYPE_BYTE:
- case SD_BUS_TYPE_BOOLEAN:
- case SD_BUS_TYPE_INT16:
- case SD_BUS_TYPE_UINT16:
- case SD_BUS_TYPE_INT32:
- case SD_BUS_TYPE_UINT32:
- case SD_BUS_TYPE_UNIX_FD:
- case SD_BUS_TYPE_INT64:
- case SD_BUS_TYPE_UINT64:
- case SD_BUS_TYPE_DOUBLE:
- break;
-
- case SD_BUS_TYPE_STRUCT_BEGIN:
- case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
- char t[n-1];
-
- memcpy(t, p + 1, n - 2);
- t[n - 2] = 0;
-
- r = bus_gvariant_is_fixed_size(t);
- if (r <= 0)
- return r;
- break;
- }
-
- default:
- assert_not_reached("Unknown signature type");
- }
-
- p += n;
- }
-
- return true;
-}
diff --git a/src/libsystemd/bus-gvariant.h b/src/libsystemd/bus-gvariant.h
deleted file mode 100644
index b4bd2a5..0000000
--- a/src/libsystemd/bus-gvariant.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-int bus_gvariant_get_size(const char *signature) _pure_;
-int bus_gvariant_get_alignment(const char *signature) _pure_;
-int bus_gvariant_is_fixed_size(const char *signature) _pure_;
diff --git a/src/libsystemd/bus-internal.c b/src/libsystemd/bus-internal.c
deleted file mode 100644
index 0bea8ca..0000000
--- a/src/libsystemd/bus-internal.c
+++ /dev/null
@@ -1,305 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "bus-internal.h"
-
-bool object_path_is_valid(const char *p) {
- const char *q;
- bool slash;
-
- if (!p)
- return false;
-
- if (p[0] != '/')
- return false;
-
- if (p[1] == 0)
- return true;
-
- for (slash = true, q = p+1; *q; q++)
- if (*q == '/') {
- if (slash)
- return false;
-
- slash = true;
- } else {
- bool good;
-
- good =
- (*q >= 'a' && *q <= 'z') ||
- (*q >= 'A' && *q <= 'Z') ||
- (*q >= '0' && *q <= '9') ||
- *q == '_';
-
- if (!good)
- return false;
-
- slash = false;
- }
-
- if (slash)
- return false;
-
- return true;
-}
-
-char* object_path_startswith(const char *a, const char *b) {
- const char *p;
-
- if (!object_path_is_valid(a) ||
- !object_path_is_valid(b))
- return NULL;
-
- if (streq(b, "/"))
- return (char*) a + 1;
-
- p = startswith(a, b);
- if (!p)
- return NULL;
-
- if (*p == 0)
- return (char*) p;
-
- if (*p == '/')
- return (char*) p + 1;
-
- return NULL;
-}
-
-bool interface_name_is_valid(const char *p) {
- const char *q;
- bool dot, found_dot = false;
-
- if (isempty(p))
- return false;
-
- for (dot = true, q = p; *q; q++)
- if (*q == '.') {
- if (dot)
- return false;
-
- found_dot = dot = true;
- } else {
- bool good;
-
- good =
- (*q >= 'a' && *q <= 'z') ||
- (*q >= 'A' && *q <= 'Z') ||
- (!dot && *q >= '0' && *q <= '9') ||
- *q == '_';
-
- if (!good)
- return false;
-
- dot = false;
- }
-
- if (q - p > 255)
- return false;
-
- if (dot)
- return false;
-
- if (!found_dot)
- return false;
-
- return true;
-}
-
-bool service_name_is_valid(const char *p) {
- const char *q;
- bool dot, found_dot = false, unique;
-
- if (isempty(p))
- return false;
-
- unique = p[0] == ':';
-
- for (dot = true, q = unique ? p+1 : p; *q; q++)
- if (*q == '.') {
- if (dot)
- return false;
-
- found_dot = dot = true;
- } else {
- bool good;
-
- good =
- (*q >= 'a' && *q <= 'z') ||
- (*q >= 'A' && *q <= 'Z') ||
- ((!dot || unique) && *q >= '0' && *q <= '9') ||
- *q == '_' || *q == '-';
-
- if (!good)
- return false;
-
- dot = false;
- }
-
- if (q - p > 255)
- return false;
-
- if (dot)
- return false;
-
- if (!found_dot)
- return false;
-
- return true;
-}
-
-bool member_name_is_valid(const char *p) {
- const char *q;
-
- if (isempty(p))
- return false;
-
- for (q = p; *q; q++) {
- bool good;
-
- good =
- (*q >= 'a' && *q <= 'z') ||
- (*q >= 'A' && *q <= 'Z') ||
- (*q >= '0' && *q <= '9') ||
- *q == '_';
-
- if (!good)
- return false;
- }
-
- if (q - p > 255)
- return false;
-
- return true;
-}
-
-static bool complex_pattern_check(char c, const char *a, const char *b) {
- bool separator = false;
-
- if (!a && !b)
- return true;
-
- if (!a || !b)
- return false;
-
- for (;;) {
- if (*a != *b)
- return (separator && (*a == 0 || *b == 0)) ||
- (*a == 0 && *b == c && b[1] == 0) ||
- (*b == 0 && *a == c && a[1] == 0);
-
- if (*a == 0)
- return true;
-
- separator = *a == c;
-
- a++, b++;
- }
-}
-
-bool namespace_complex_pattern(const char *pattern, const char *value) {
- return complex_pattern_check('.', pattern, value);
-}
-
-bool path_complex_pattern(const char *pattern, const char *value) {
- return complex_pattern_check('/', pattern, value);
-}
-
-static bool simple_pattern_check(char c, const char *a, const char *b) {
-
- if (!a && !b)
- return true;
-
- if (!a || !b)
- return false;
-
- for (;;) {
- if (*a != *b)
- return *a == 0 && *b == c;
-
- if (*a == 0)
- return true;
-
- a++, b++;
- }
-}
-
-bool namespace_simple_pattern(const char *pattern, const char *value) {
- return simple_pattern_check('.', pattern, value);
-}
-
-bool path_simple_pattern(const char *pattern, const char *value) {
- return simple_pattern_check('/', pattern, value);
-}
-
-int bus_message_type_from_string(const char *s, uint8_t *u) {
- if (streq(s, "signal"))
- *u = SD_BUS_MESSAGE_SIGNAL;
- else if (streq(s, "method_call"))
- *u = SD_BUS_MESSAGE_METHOD_CALL;
- else if (streq(s, "error"))
- *u = SD_BUS_MESSAGE_METHOD_ERROR;
- else if (streq(s, "method_return"))
- *u = SD_BUS_MESSAGE_METHOD_RETURN;
- else
- return -EINVAL;
-
- return 0;
-}
-
-const char *bus_message_type_to_string(uint8_t u) {
- if (u == SD_BUS_MESSAGE_SIGNAL)
- return "signal";
- else if (u == SD_BUS_MESSAGE_METHOD_CALL)
- return "method_call";
- else if (u == SD_BUS_MESSAGE_METHOD_ERROR)
- return "error";
- else if (u == SD_BUS_MESSAGE_METHOD_RETURN)
- return "method_return";
- else
- return NULL;
-}
-
-char *bus_address_escape(const char *v) {
- const char *a;
- char *r, *b;
-
- r = new(char, strlen(v)*3+1);
- if (!r)
- return NULL;
-
- for (a = v, b = r; *a; a++) {
-
- if ((*a >= '0' && *a <= '9') ||
- (*a >= 'a' && *a <= 'z') ||
- (*a >= 'A' && *a <= 'Z') ||
- strchr("_-/.", *a))
- *(b++) = *a;
- else {
- *(b++) = '%';
- *(b++) = hexchar(*a >> 4);
- *(b++) = hexchar(*a & 0xF);
- }
- }
-
- *b = 0;
- return r;
-}
diff --git a/src/libsystemd/bus-internal.h b/src/libsystemd/bus-internal.h
deleted file mode 100644
index 7c92293..0000000
--- a/src/libsystemd/bus-internal.h
+++ /dev/null
@@ -1,328 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <netinet/in.h>
-#include <pthread.h>
-
-#include "hashmap.h"
-#include "prioq.h"
-#include "list.h"
-#include "util.h"
-#include "refcnt.h"
-
-#include "sd-bus.h"
-#include "bus-error.h"
-#include "bus-match.h"
-#include "bus-kernel.h"
-#include "kdbus.h"
-
-struct reply_callback {
- sd_bus_message_handler_t callback;
- void *userdata;
- usec_t timeout;
- uint64_t cookie;
- unsigned prioq_idx;
-};
-
-struct filter_callback {
- sd_bus_message_handler_t callback;
- void *userdata;
-
- unsigned last_iteration;
-
- LIST_FIELDS(struct filter_callback, callbacks);
-};
-
-struct node {
- char *path;
- struct node *parent;
- LIST_HEAD(struct node, child);
- LIST_FIELDS(struct node, siblings);
-
- LIST_HEAD(struct node_callback, callbacks);
- LIST_HEAD(struct node_vtable, vtables);
- LIST_HEAD(struct node_enumerator, enumerators);
-
- bool object_manager;
-};
-
-struct node_callback {
- struct node *node;
-
- bool is_fallback;
- sd_bus_message_handler_t callback;
- void *userdata;
-
- unsigned last_iteration;
-
- LIST_FIELDS(struct node_callback, callbacks);
-};
-
-struct node_enumerator {
- struct node *node;
-
- sd_bus_node_enumerator_t callback;
- void *userdata;
-
- unsigned last_iteration;
-
- LIST_FIELDS(struct node_enumerator, enumerators);
-};
-
-struct node_vtable {
- struct node *node;
-
- char *interface;
- bool is_fallback;
- const sd_bus_vtable *vtable;
- void *userdata;
- sd_bus_object_find_t find;
-
- unsigned last_iteration;
-
- LIST_FIELDS(struct node_vtable, vtables);
-};
-
-struct vtable_member {
- const char *path;
- const char *interface;
- const char *member;
- struct node_vtable *parent;
- unsigned last_iteration;
- const sd_bus_vtable *vtable;
-};
-
-enum bus_state {
- BUS_UNSET,
- BUS_OPENING,
- BUS_AUTHENTICATING,
- BUS_HELLO,
- BUS_RUNNING,
- BUS_CLOSING,
- BUS_CLOSED
-};
-
-static inline bool BUS_IS_OPEN(enum bus_state state) {
- return state > BUS_UNSET && state < BUS_CLOSING;
-}
-
-enum bus_auth {
- _BUS_AUTH_INVALID,
- BUS_AUTH_EXTERNAL,
- BUS_AUTH_ANONYMOUS
-};
-
-struct sd_bus {
- /* We use atomic ref counting here since sd_bus_message
- objects retain references to their originating sd_bus but
- we want to allow them to be processed in a different
- thread. We won't provide full thread safety, but only the
- bare minimum that makes it possible to use sd_bus and
- sd_bus_message objects independently and on different
- threads as long as each object is used only once at the
- same time. */
- RefCount n_ref;
-
- enum bus_state state;
- int input_fd, output_fd;
- int message_version;
- int message_endian;
-
- bool is_kernel:1;
- bool can_fds:1;
- bool bus_client:1;
- bool ucred_valid:1;
- bool is_server:1;
- bool anonymous_auth:1;
- bool prefer_readv:1;
- bool prefer_writev:1;
- bool match_callbacks_modified:1;
- bool filter_callbacks_modified:1;
- bool nodes_modified:1;
- bool trusted:1;
- bool fake_creds_valid:1;
- bool manual_peer_interface:1;
-
- int use_memfd;
-
- void *rbuffer;
- size_t rbuffer_size;
-
- sd_bus_message **rqueue;
- unsigned rqueue_size;
- size_t rqueue_allocated;
-
- sd_bus_message **wqueue;
- unsigned wqueue_size;
- size_t windex;
- size_t wqueue_allocated;
-
- uint64_t cookie;
-
- char *unique_name;
- uint64_t unique_id;
-
- struct bus_match_node match_callbacks;
- Prioq *reply_callbacks_prioq;
- Hashmap *reply_callbacks;
- LIST_HEAD(struct filter_callback, filter_callbacks);
-
- Hashmap *nodes;
- Hashmap *vtable_methods;
- Hashmap *vtable_properties;
-
- union {
- struct sockaddr sa;
- struct sockaddr_un un;
- struct sockaddr_in in;
- struct sockaddr_in6 in6;
- } sockaddr;
- socklen_t sockaddr_size;
-
- char *kernel;
- char *machine;
-
- sd_id128_t server_id;
-
- char *address;
- unsigned address_index;
-
- int last_connect_error;
-
- enum bus_auth auth;
- size_t auth_rbegin;
- struct iovec auth_iovec[3];
- unsigned auth_index;
- char *auth_buffer;
- usec_t auth_timeout;
-
- struct ucred ucred;
- char label[NAME_MAX];
-
- uint64_t creds_mask;
-
- int *fds;
- unsigned n_fds;
-
- char *exec_path;
- char **exec_argv;
-
- uint64_t hello_cookie;
- unsigned iteration_counter;
-
- void *kdbus_buffer;
-
- /* We do locking around the memfd cache, since we want to
- * allow people to process a sd_bus_message in a different
- * thread then it was generated on and free it there. Since
- * adding something to the memfd cache might happen when a
- * message is released, we hence need to protect this bit with
- * a mutex. */
- pthread_mutex_t memfd_cache_mutex;
- struct memfd_cache memfd_cache[MEMFD_CACHE_MAX];
- unsigned n_memfd_cache;
-
- pid_t original_pid;
-
- uint64_t hello_flags;
- uint64_t attach_flags;
-
- uint64_t match_cookie;
-
- sd_event_source *input_io_event_source;
- sd_event_source *output_io_event_source;
- sd_event_source *time_event_source;
- sd_event_source *quit_event_source;
- sd_event *event;
- int event_priority;
-
- sd_bus_message *current;
-
- sd_bus **default_bus_ptr;
- pid_t tid;
-
- struct kdbus_creds fake_creds;
- char *fake_label;
-
- char *cgroup_root;
-};
-
-#define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
-
-#define BUS_WQUEUE_MAX 1024
-#define BUS_RQUEUE_MAX 64*1024
-
-#define BUS_MESSAGE_SIZE_MAX (64*1024*1024)
-#define BUS_AUTH_SIZE_MAX (64*1024)
-
-#define BUS_CONTAINER_DEPTH 128
-
-/* Defined by the specification as maximum size of an array in
- * bytes */
-#define BUS_ARRAY_MAX_SIZE 67108864
-
-#define BUS_FDS_MAX 1024
-
-#define BUS_EXEC_ARGV_MAX 256
-
-bool interface_name_is_valid(const char *p) _pure_;
-bool service_name_is_valid(const char *p) _pure_;
-bool member_name_is_valid(const char *p) _pure_;
-bool object_path_is_valid(const char *p) _pure_;
-char *object_path_startswith(const char *a, const char *b) _pure_;
-
-bool namespace_complex_pattern(const char *pattern, const char *value) _pure_;
-bool path_complex_pattern(const char *pattern, const char *value) _pure_;
-
-bool namespace_simple_pattern(const char *pattern, const char *value) _pure_;
-bool path_simple_pattern(const char *pattern, const char *value) _pure_;
-
-int bus_message_type_from_string(const char *s, uint8_t *u) _pure_;
-const char *bus_message_type_to_string(uint8_t u) _pure_;
-
-#define error_name_is_valid interface_name_is_valid
-
-int bus_ensure_running(sd_bus *bus);
-int bus_start_running(sd_bus *bus);
-int bus_next_address(sd_bus *bus);
-
-int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m);
-
-int bus_rqueue_make_room(sd_bus *bus);
-
-bool bus_pid_changed(sd_bus *bus);
-
-char *bus_address_escape(const char *v);
-
-#define OBJECT_PATH_FOREACH_PREFIX(prefix, path) \
- for (char *_slash = ({ strcpy((prefix), (path)); streq((prefix), "/") ? NULL : strrchr((prefix), '/'); }) ; \
- _slash && !(_slash[(_slash) == (prefix)] = 0); \
- _slash = streq((prefix), "/") ? NULL : strrchr((prefix), '/'))
-
-/* If we are invoking callbacks of a bus object, ensure unreffing the
- * bus from the callback doesn't destroy the object we are working
- * on */
-#define BUS_DONT_DESTROY(bus) \
- _cleanup_bus_unref_ _unused_ sd_bus *_dont_destroy_##bus = sd_bus_ref(bus)
diff --git a/src/libsystemd/bus-introspect.c b/src/libsystemd/bus-introspect.c
deleted file mode 100644
index d528ab2..0000000
--- a/src/libsystemd/bus-introspect.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-#include "sd-bus-protocol.h"
-#include "bus-introspect.h"
-#include "bus-signature.h"
-#include "bus-internal.h"
-#include "bus-protocol.h"
-
-int introspect_begin(struct introspect *i, bool trusted) {
- assert(i);
-
- zero(*i);
- i->trusted = trusted;
-
- i->f = open_memstream(&i->introspection, &i->size);
- if (!i->f)
- return -ENOMEM;
-
- fputs(BUS_INTROSPECT_DOCTYPE
- "<node>\n", i->f);
-
- return 0;
-}
-
-int introspect_write_default_interfaces(struct introspect *i, bool object_manager) {
- assert(i);
-
- fputs(BUS_INTROSPECT_INTERFACE_PEER
- BUS_INTROSPECT_INTERFACE_INTROSPECTABLE
- BUS_INTROSPECT_INTERFACE_PROPERTIES, i->f);
-
- if (object_manager)
- fputs(BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER, i->f);
-
- return 0;
-}
-
-int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) {
- char *node;
-
- assert(i);
- assert(prefix);
-
- while ((node = set_steal_first(s))) {
- const char *e;
-
- e = object_path_startswith(node, prefix);
- if (e && e[0])
- fprintf(i->f, " <node name=\"%s\"/>\n", e);
-
- free(node);
- }
-
- return 0;
-}
-
-static void introspect_write_flags(struct introspect *i, int type, int flags) {
- if (flags & SD_BUS_VTABLE_DEPRECATED)
- fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f);
-
- if (type == _SD_BUS_VTABLE_METHOD && (flags & SD_BUS_VTABLE_METHOD_NO_REPLY))
- fputs(" <annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n", i->f);
-
- if (type == _SD_BUS_VTABLE_PROPERTY || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) {
- if (flags & SD_BUS_VTABLE_PROPERTY_CONST)
- fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"const\"/>\n", i->f);
- else if (flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)
- fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"invalidates\"/>\n", i->f);
- else if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
- fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"false\"/>\n", i->f);
- }
-
- if (!i->trusted &&
- (type == _SD_BUS_VTABLE_METHOD || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) &&
- !(flags & SD_BUS_VTABLE_UNPRIVILEGED))
- fputs(" <annotation name=\"org.freedesktop.systemd1.Privileged\" value=\"true\"/>\n", i->f);
-}
-
-static int introspect_write_arguments(struct introspect *i, const char *signature, const char *direction) {
- int r;
-
- for (;;) {
- size_t l;
-
- if (!*signature)
- return 0;
-
- r = signature_element_length(signature, &l);
- if (r < 0)
- return r;
-
- fprintf(i->f, " <arg type=\"%.*s\"", (int) l, signature);
-
- if (direction)
- fprintf(i->f, " direction=\"%s\"/>\n", direction);
- else
- fputs("/>\n", i->f);
-
- signature += l;
- }
-}
-
-int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) {
- assert(i);
- assert(v);
-
- for (; v->type != _SD_BUS_VTABLE_END; v++) {
-
- /* Ignore methods, signals and properties that are
- * marked "hidden", but do show the interface
- * itself */
-
- if (v->type != _SD_BUS_VTABLE_START && (v->flags & SD_BUS_VTABLE_HIDDEN))
- continue;
-
- switch (v->type) {
-
- case _SD_BUS_VTABLE_START:
- if (v->flags & SD_BUS_VTABLE_DEPRECATED)
- fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f);
- break;
-
- case _SD_BUS_VTABLE_METHOD:
- fprintf(i->f, " <method name=\"%s\">\n", v->x.method.member);
- introspect_write_arguments(i, strempty(v->x.method.signature), "in");
- introspect_write_arguments(i, strempty(v->x.method.result), "out");
- introspect_write_flags(i, v->type, v->flags);
- fputs(" </method>\n", i->f);
- break;
-
- case _SD_BUS_VTABLE_PROPERTY:
- case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
- fprintf(i->f, " <property name=\"%s\" type=\"%s\" access=\"%s\">\n",
- v->x.property.member,
- v->x.property.signature,
- v->type == _SD_BUS_VTABLE_WRITABLE_PROPERTY ? "readwrite" : "read");
- introspect_write_flags(i, v->type, v->flags);
- fputs(" </property>\n", i->f);
- break;
-
- case _SD_BUS_VTABLE_SIGNAL:
- fprintf(i->f, " <signal name=\"%s\">\n", v->x.signal.member);
- introspect_write_arguments(i, strempty(v->x.signal.signature), NULL);
- introspect_write_flags(i, v->type, v->flags);
- fputs(" </signal>\n", i->f);
- break;
- }
-
- }
-
- return 0;
-}
-
-int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_bus_message **reply) {
- sd_bus_message *q;
- int r;
-
- assert(i);
- assert(m);
- assert(reply);
-
- fputs("</node>\n", i->f);
- fflush(i->f);
-
- if (ferror(i->f))
- return -ENOMEM;
-
- r = sd_bus_message_new_method_return(m, &q);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(q, "s", i->introspection);
- if (r < 0) {
- sd_bus_message_unref(q);
- return r;
- }
-
- *reply = q;
- return 0;
-}
-
-void introspect_free(struct introspect *i) {
- assert(i);
-
- if (i->f)
- fclose(i->f);
-
- if (i->introspection)
- free(i->introspection);
-
- zero(*i);
-}
diff --git a/src/libsystemd/bus-introspect.h b/src/libsystemd/bus-introspect.h
deleted file mode 100644
index 98312d1..0000000
--- a/src/libsystemd/bus-introspect.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/types.h>
-#include <stdio.h>
-
-#include "sd-bus.h"
-#include "set.h"
-
-struct introspect {
- FILE *f;
- char *introspection;
- size_t size;
- bool trusted;
-};
-
-int introspect_begin(struct introspect *i, bool trusted);
-int introspect_write_default_interfaces(struct introspect *i, bool object_manager);
-int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix);
-int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v);
-int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_bus_message **reply);
-void introspect_free(struct introspect *i);
diff --git a/src/libsystemd/bus-kernel.c b/src/libsystemd/bus-kernel.c
deleted file mode 100644
index f85b4d5..0000000
--- a/src/libsystemd/bus-kernel.c
+++ /dev/null
@@ -1,1348 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#ifdef HAVE_VALGRIND_MEMCHECK_H
-#include <valgrind/memcheck.h>
-#endif
-
-#include <fcntl.h>
-#include <malloc.h>
-#include <sys/mman.h>
-
-#include "util.h"
-#include "strv.h"
-
-#include "bus-internal.h"
-#include "bus-message.h"
-#include "bus-kernel.h"
-#include "bus-bloom.h"
-#include "bus-util.h"
-#include "cgroup-util.h"
-
-#define UNIQUE_NAME_MAX (3+DECIMAL_STR_MAX(uint64_t))
-
-int bus_kernel_parse_unique_name(const char *s, uint64_t *id) {
- int r;
-
- assert(s);
- assert(id);
-
- if (!startswith(s, ":1."))
- return 0;
-
- r = safe_atou64(s + 3, id);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static void append_payload_vec(struct kdbus_item **d, const void *p, size_t sz) {
- assert(d);
- assert(sz > 0);
-
- *d = ALIGN8_PTR(*d);
-
- /* Note that p can be NULL, which encodes a region full of
- * zeroes, which is useful to optimize certain padding
- * conditions */
-
- (*d)->size = offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec);
- (*d)->type = KDBUS_ITEM_PAYLOAD_VEC;
- (*d)->vec.address = PTR_TO_UINT64(p);
- (*d)->vec.size = sz;
-
- *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
-}
-
-static void append_payload_memfd(struct kdbus_item **d, int memfd, size_t sz) {
- assert(d);
- assert(memfd >= 0);
- assert(sz > 0);
-
- *d = ALIGN8_PTR(*d);
- (*d)->size = offsetof(struct kdbus_item, memfd) + sizeof(struct kdbus_memfd);
- (*d)->type = KDBUS_ITEM_PAYLOAD_MEMFD;
- (*d)->memfd.fd = memfd;
- (*d)->memfd.size = sz;
-
- *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
-}
-
-static void append_destination(struct kdbus_item **d, const char *s, size_t length) {
- assert(d);
- assert(s);
-
- *d = ALIGN8_PTR(*d);
-
- (*d)->size = offsetof(struct kdbus_item, str) + length + 1;
- (*d)->type = KDBUS_ITEM_DST_NAME;
- memcpy((*d)->str, s, length + 1);
-
- *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
-}
-
-static void* append_bloom(struct kdbus_item **d, size_t length) {
- void *r;
-
- assert(d);
-
- *d = ALIGN8_PTR(*d);
-
- (*d)->size = offsetof(struct kdbus_item, data) + length;
- (*d)->type = KDBUS_ITEM_BLOOM;
- r = (*d)->data;
-
- *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
-
- return r;
-}
-
-static void append_fds(struct kdbus_item **d, const int fds[], unsigned n_fds) {
- assert(d);
- assert(fds);
- assert(n_fds > 0);
-
- *d = ALIGN8_PTR(*d);
- (*d)->size = offsetof(struct kdbus_item, fds) + sizeof(int) * n_fds;
- (*d)->type = KDBUS_ITEM_FDS;
- memcpy((*d)->fds, fds, sizeof(int) * n_fds);
-
- *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
-}
-
-static int bus_message_setup_bloom(sd_bus_message *m, void *bloom) {
- unsigned i;
- int r;
-
- assert(m);
- assert(bloom);
-
- memset(bloom, 0, BLOOM_SIZE);
-
- bloom_add_pair(bloom, "message-type", bus_message_type_to_string(m->header->type));
-
- if (m->interface)
- bloom_add_pair(bloom, "interface", m->interface);
- if (m->member)
- bloom_add_pair(bloom, "member", m->member);
- if (m->path) {
- bloom_add_pair(bloom, "path", m->path);
- bloom_add_pair(bloom, "path-slash-prefix", m->path);
- bloom_add_prefixes(bloom, "path-slash-prefix", m->path, '/');
- }
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- for (i = 0; i < 64; i++) {
- char type;
- const char *t;
- char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")];
- char *e;
-
- r = sd_bus_message_peek_type(m, &type, NULL);
- if (r < 0)
- return r;
-
- if (type != SD_BUS_TYPE_STRING &&
- type != SD_BUS_TYPE_OBJECT_PATH &&
- type != SD_BUS_TYPE_SIGNATURE)
- break;
-
- r = sd_bus_message_read_basic(m, type, &t);
- if (r < 0)
- return r;
-
- e = stpcpy(buf, "arg");
- if (i < 10)
- *(e++) = '0' + (char) i;
- else {
- *(e++) = '0' + (char) (i / 10);
- *(e++) = '0' + (char) (i % 10);
- }
-
- *e = 0;
- bloom_add_pair(bloom, buf, t);
-
- strcpy(e, "-dot-prefix");
- bloom_add_prefixes(bloom, buf, t, '.');
- strcpy(e, "-slash-prefix");
- bloom_add_prefixes(bloom, buf, t, '/');
- }
-
- return 0;
-}
-
-static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
- struct bus_body_part *part;
- struct kdbus_item *d;
- bool well_known;
- uint64_t unique;
- size_t sz, dl;
- unsigned i;
- int r;
-
- assert(b);
- assert(m);
- assert(m->sealed);
-
- if (m->kdbus)
- return 0;
-
- if (m->destination) {
- r = bus_kernel_parse_unique_name(m->destination, &unique);
- if (r < 0)
- return r;
-
- well_known = r == 0;
- } else
- well_known = false;
-
- sz = offsetof(struct kdbus_msg, items);
-
- assert_cc(ALIGN8(offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec)) ==
- ALIGN8(offsetof(struct kdbus_item, memfd) + sizeof(struct kdbus_memfd)));
-
- /* Add in fixed header, fields header and payload */
- sz += (1 + m->n_body_parts) *
- ALIGN8(offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec));
-
- /* Add space for bloom filter */
- sz += ALIGN8(offsetof(struct kdbus_item, data) + BLOOM_SIZE);
-
- /* Add in well-known destination header */
- if (well_known) {
- dl = strlen(m->destination);
- sz += ALIGN8(offsetof(struct kdbus_item, str) + dl + 1);
- }
-
- /* Add space for unix fds */
- if (m->n_fds > 0)
- sz += ALIGN8(offsetof(struct kdbus_item, fds) + sizeof(int)*m->n_fds);
-
- m->kdbus = memalign(8, sz);
- if (!m->kdbus) {
- r = -ENOMEM;
- goto fail;
- }
-
- m->free_kdbus = true;
- memset(m->kdbus, 0, sz);
-
- m->kdbus->flags =
- ((m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) |
- ((m->header->flags & BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0);
- m->kdbus->dst_id =
- well_known ? 0 :
- m->destination ? unique : KDBUS_DST_ID_BROADCAST;
- m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS;
- m->kdbus->cookie = m->header->serial;
-
- if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
- m->kdbus->cookie_reply = m->reply_cookie;
- else
- m->kdbus->timeout_ns = m->timeout * NSEC_PER_USEC;
-
- d = m->kdbus->items;
-
- if (well_known)
- append_destination(&d, m->destination, dl);
-
- append_payload_vec(&d, m->header, BUS_MESSAGE_BODY_BEGIN(m));
-
- MESSAGE_FOREACH_PART(part, i, m) {
- if (part->is_zero) {
- /* If this is padding then simply send a
- * vector with a NULL data pointer which the
- * kernel will just pass through. This is the
- * most efficient way to encode zeroes */
-
- append_payload_vec(&d, NULL, part->size);
- continue;
- }
-
- if (part->memfd >= 0 && part->sealed && m->destination) {
- /* Try to send a memfd, if the part is
- * sealed and this is not a broadcast. Since we can only */
-
- append_payload_memfd(&d, part->memfd, part->size);
- continue;
- }
-
- /* Otherwise let's send a vector to the actual data,
- * for that we need to map it first. */
- r = bus_body_part_map(part);
- if (r < 0)
- goto fail;
-
- append_payload_vec(&d, part->data, part->size);
- }
-
- if (m->kdbus->dst_id == KDBUS_DST_ID_BROADCAST) {
- void *p;
-
- p = append_bloom(&d, BLOOM_SIZE);
- r = bus_message_setup_bloom(m, p);
- if (r < 0)
- goto fail;
- }
-
- if (m->n_fds > 0)
- append_fds(&d, m->fds, m->n_fds);
-
- m->kdbus->size = (uint8_t*) d - (uint8_t*) m->kdbus;
- assert(m->kdbus->size <= sz);
-
- return 0;
-
-fail:
- m->poisoned = true;
- return r;
-}
-
-static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
- sd_bus_message *m = NULL;
- struct kdbus_item *d;
- unsigned n_fds = 0;
- _cleanup_free_ int *fds = NULL;
- struct bus_header *h = NULL;
- size_t total, n_bytes = 0, idx = 0;
- const char *destination = NULL, *seclabel = NULL;
- int r;
-
- assert(bus);
- assert(k);
- assert(k->payload_type == KDBUS_PAYLOAD_DBUS);
-
- KDBUS_ITEM_FOREACH(d, k, items) {
- size_t l;
-
- l = d->size - offsetof(struct kdbus_item, data);
-
- switch (d->type) {
-
- case KDBUS_ITEM_PAYLOAD_OFF:
- if (!h) {
- h = (struct bus_header *)((uint8_t *)k + d->vec.offset);
-
- if (!bus_header_is_complete(h, d->vec.size))
- return -EBADMSG;
- }
-
- n_bytes += d->vec.size;
- break;
-
- case KDBUS_ITEM_PAYLOAD_MEMFD:
- if (!h)
- return -EBADMSG;
-
- n_bytes += d->memfd.size;
- break;
-
- case KDBUS_ITEM_FDS: {
- int *f;
- unsigned j;
-
- j = l / sizeof(int);
- f = realloc(fds, sizeof(int) * (n_fds + j));
- if (!f)
- return -ENOMEM;
-
- fds = f;
- memcpy(fds + n_fds, d->fds, sizeof(int) * j);
- n_fds += j;
- break;
- }
-
- case KDBUS_ITEM_SECLABEL:
- seclabel = d->str;
- break;
- }
- }
-
- if (!h)
- return -EBADMSG;
-
- r = bus_header_message_size(h, &total);
- if (r < 0)
- return r;
-
- if (n_bytes != total)
- return -EBADMSG;
-
- /* on kdbus we only speak native endian gvariant, never dbus1
- * marshalling or reverse endian */
- if (h->version != 2 ||
- h->endian != BUS_NATIVE_ENDIAN)
- return -EPROTOTYPE;
-
- r = bus_message_from_header(bus, h, sizeof(struct bus_header), fds, n_fds, NULL, seclabel, 0, &m);
- if (r < 0)
- return r;
-
- /* The well-known names list is different from the other
- credentials. If we asked for it, but nothing is there, this
- means that the list of well-known names is simply empty, not
- that we lack any data */
-
- m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask;
-
- KDBUS_ITEM_FOREACH(d, k, items) {
- size_t l;
-
- l = d->size - offsetof(struct kdbus_item, data);
-
- switch (d->type) {
-
- case KDBUS_ITEM_PAYLOAD_OFF: {
- size_t begin_body;
-
- begin_body = BUS_MESSAGE_BODY_BEGIN(m);
-
- if (idx + d->vec.size > begin_body) {
- struct bus_body_part *part;
-
- /* Contains body material */
-
- part = message_append_part(m);
- if (!part) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* A -1 offset is NUL padding. */
- part->is_zero = d->vec.offset == ~0ULL;
-
- if (idx >= begin_body) {
- if (!part->is_zero)
- part->data = (uint8_t *)k + d->vec.offset;
- part->size = d->vec.size;
- } else {
- if (!part->is_zero)
- part->data = (uint8_t *)k + d->vec.offset + (begin_body - idx);
- part->size = d->vec.size - (begin_body - idx);
- }
-
- part->sealed = true;
- }
-
- idx += d->vec.size;
- break;
- }
-
- case KDBUS_ITEM_PAYLOAD_MEMFD: {
- struct bus_body_part *part;
-
- if (idx < BUS_MESSAGE_BODY_BEGIN(m)) {
- r = -EBADMSG;
- goto fail;
- }
-
- part = message_append_part(m);
- if (!part) {
- r = -ENOMEM;
- goto fail;
- }
-
- part->memfd = d->memfd.fd;
- part->size = d->memfd.size;
- part->sealed = true;
-
- idx += d->memfd.size;
- break;
- }
-
- case KDBUS_ITEM_CREDS:
- /* UID/GID/PID are always valid */
- m->creds.uid = (uid_t) d->creds.uid;
- m->creds.gid = (gid_t) d->creds.gid;
- m->creds.pid = (pid_t) d->creds.pid;
- m->creds.mask |= (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID) & bus->creds_mask;
-
- /* The PID starttime/TID might be missing
- * however, when the data is faked by some
- * data bus proxy and it lacks that
- * information about the real client since
- * SO_PEERCRED is used for that */
-
- if (d->creds.starttime > 0) {
- m->creds.pid_starttime = d->creds.starttime / NSEC_PER_USEC;
- m->creds.mask |= SD_BUS_CREDS_PID_STARTTIME & bus->creds_mask;
- }
-
- if (d->creds.tid > 0) {
- m->creds.tid = (pid_t) d->creds.tid;
- m->creds.mask |= SD_BUS_CREDS_TID & bus->creds_mask;
- }
- break;
-
- case KDBUS_ITEM_TIMESTAMP:
- m->realtime = d->timestamp.realtime_ns / NSEC_PER_USEC;
- m->monotonic = d->timestamp.monotonic_ns / NSEC_PER_USEC;
- break;
-
- case KDBUS_ITEM_PID_COMM:
- m->creds.comm = d->str;
- m->creds.mask |= SD_BUS_CREDS_COMM & bus->creds_mask;
- break;
-
- case KDBUS_ITEM_TID_COMM:
- m->creds.tid_comm = d->str;
- m->creds.mask |= SD_BUS_CREDS_TID_COMM & bus->creds_mask;
- break;
-
- case KDBUS_ITEM_EXE:
- m->creds.exe = d->str;
- m->creds.mask |= SD_BUS_CREDS_EXE & bus->creds_mask;
- break;
-
- case KDBUS_ITEM_CMDLINE:
- m->creds.cmdline = d->str;
- m->creds.cmdline_size = l;
- m->creds.mask |= SD_BUS_CREDS_CMDLINE & bus->creds_mask;
- break;
-
- case KDBUS_ITEM_CGROUP:
- m->creds.cgroup = d->str;
- m->creds.mask |= (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID) & bus->creds_mask;
-
- if (!bus->cgroup_root) {
- r = cg_get_root_path(&bus->cgroup_root);
- if (r < 0)
- goto fail;
- }
-
- m->creds.cgroup_root = bus->cgroup_root;
-
- break;
-
- case KDBUS_ITEM_AUDIT:
- m->creds.audit_session_id = (uint32_t) d->audit.sessionid;
- m->creds.audit_login_uid = (uid_t) d->audit.loginuid;
- m->creds.mask |= (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID) & bus->creds_mask;
- break;
-
- case KDBUS_ITEM_CAPS:
- m->creds.capability = d->data;
- m->creds.capability_size = l;
- m->creds.mask |= (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS) & bus->creds_mask;
- break;
-
- case KDBUS_ITEM_DST_NAME:
- if (!service_name_is_valid(d->str))
- return -EBADMSG;
-
- destination = d->str;
- break;
-
- case KDBUS_ITEM_NAME:
- if (!service_name_is_valid(d->name.name))
- return -EBADMSG;
-
- r = strv_extend(&m->creds.well_known_names, d->name.name);
- if (r < 0)
- goto fail;
- break;
-
- case KDBUS_ITEM_FDS:
- case KDBUS_ITEM_SECLABEL:
- break;
-
- default:
- log_debug("Got unknown field from kernel %llu", d->type);
- }
- }
-
- r = bus_message_parse_fields(m);
- if (r < 0)
- goto fail;
-
- /* Override information from the user header with data from the kernel */
- if (k->src_id == KDBUS_SRC_ID_KERNEL)
- m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus";
- else {
- snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id);
- m->sender = m->creds.unique_name = m->sender_buffer;
- }
-
- if (destination)
- m->destination = destination;
- else if (k->dst_id == KDBUS_DST_ID_BROADCAST)
- m->destination = NULL;
- else if (k->dst_id == KDBUS_DST_ID_NAME)
- m->destination = bus->unique_name; /* fill in unique name if the well-known name is missing */
- else {
- snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id);
- m->destination = m->destination_buffer;
- }
-
- /* We take possession of the kmsg struct now */
- m->kdbus = k;
- m->release_kdbus = true;
- m->free_fds = true;
- fds = NULL;
-
- bus->rqueue[bus->rqueue_size++] = m;
-
- return 1;
-
-fail:
- if (m) {
- struct bus_body_part *part;
- unsigned i;
-
- /* Make sure the memfds are not freed twice */
- MESSAGE_FOREACH_PART(part, i, m)
- if (part->memfd >= 0)
- part->memfd = -1;
-
- sd_bus_message_unref(m);
- }
-
- return r;
-}
-
-int bus_kernel_take_fd(sd_bus *b) {
- struct kdbus_cmd_hello *hello;
- struct kdbus_item *item;
- size_t l = 0, sz;
- int r;
-
- assert(b);
-
- if (b->is_server)
- return -EINVAL;
-
- b->use_memfd = 1;
-
- sz = ALIGN8(offsetof(struct kdbus_cmd_hello, items));
-
- if (b->fake_creds_valid)
- sz += ALIGN8(offsetof(struct kdbus_item, creds)) + sizeof(struct kdbus_creds);
-
- if (b->fake_label) {
- l = strlen(b->fake_label);
- sz += ALIGN8(offsetof(struct kdbus_item, str) + l + 1);
- }
-
- hello = alloca0(sz);
- hello->size = sz;
- hello->conn_flags = b->hello_flags;
- hello->attach_flags = b->attach_flags;
- hello->pool_size = KDBUS_POOL_SIZE;
-
- item = hello->items;
-
- if (b->fake_creds_valid) {
- item->size = offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds);
- item->type = KDBUS_ITEM_CREDS;
- item->creds = b->fake_creds;
-
- item = KDBUS_ITEM_NEXT(item);
- }
-
- if (b->fake_label) {
- item->size = offsetof(struct kdbus_item, str) + l + 1;
- memcpy(item->str, b->fake_label, l+1);
- }
-
- r = ioctl(b->input_fd, KDBUS_CMD_HELLO, hello);
- if (r < 0)
- return -errno;
-
- if (!b->kdbus_buffer) {
- b->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, b->input_fd, 0);
- if (b->kdbus_buffer == MAP_FAILED) {
- b->kdbus_buffer = NULL;
- return -errno;
- }
- }
-
- /* The higher 32bit of both flags fields are considered
- * 'incompatible flags'. Refuse them all for now. */
- if (hello->bus_flags > 0xFFFFFFFFULL ||
- hello->conn_flags > 0xFFFFFFFFULL)
- return -ENOTSUP;
-
- if (hello->bloom_size != BLOOM_SIZE)
- return -ENOTSUP;
-
- if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello->id) < 0)
- return -ENOMEM;
-
- b->unique_id = hello->id;
-
- b->is_kernel = true;
- b->bus_client = true;
- b->can_fds = !!(hello->conn_flags & KDBUS_HELLO_ACCEPT_FD);
- b->message_version = 2;
- b->message_endian = BUS_NATIVE_ENDIAN;
-
- /* the kernel told us the UUID of the underlying bus */
- memcpy(b->server_id.bytes, hello->id128, sizeof(b->server_id.bytes));
-
- return bus_start_running(b);
-}
-
-int bus_kernel_connect(sd_bus *b) {
- assert(b);
- assert(b->input_fd < 0);
- assert(b->output_fd < 0);
- assert(b->kernel);
-
- if (b->is_server)
- return -EINVAL;
-
- b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (b->input_fd < 0)
- return -errno;
-
- b->output_fd = b->input_fd;
-
- return bus_kernel_take_fd(b);
-}
-
-int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
- int r;
-
- assert(bus);
- assert(m);
- assert(bus->state == BUS_RUNNING);
-
- /* If we can't deliver, we want room for the error message */
- r = bus_rqueue_make_room(bus);
- if (r < 0)
- return r;
-
- r = bus_message_setup_kmsg(bus, m);
- if (r < 0)
- return r;
-
- r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus);
- if (r < 0) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- sd_bus_message *reply;
-
- if (errno == EAGAIN || errno == EINTR)
- return 0;
- else if (errno == ENXIO || errno == ESRCH) {
-
- /* ENXIO: unique name not known
- * ESRCH: well-known name not known */
-
- if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
- sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Destination %s not known", m->destination);
- else {
- log_debug("Could not deliver message to %s as destination is not known. Ignoring.", m->destination);
- return 0;
- }
-
- } else if (errno == EADDRNOTAVAIL) {
-
- /* EADDRNOTAVAIL: activation is possible, but turned off in request flags */
-
- if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
- sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Activation of %s not requested", m->destination);
- else {
- log_debug("Could not deliver message to %s as destination is not activated. Ignoring.", m->destination);
- return 0;
- }
- } else
- return -errno;
-
- r = bus_message_new_synthetic_error(
- bus,
- BUS_MESSAGE_COOKIE(m),
- &error,
- &reply);
-
- if (r < 0)
- return r;
-
- r = bus_seal_synthetic_message(bus, reply);
- if (r < 0)
- return r;
-
- bus->rqueue[bus->rqueue_size++] = reply;
-
- return 0;
- }
-
- return 1;
-}
-
-static void close_kdbus_msg(sd_bus *bus, struct kdbus_msg *k) {
- uint64_t off;
- struct kdbus_item *d;
-
- assert(bus);
- assert(k);
-
- off = (uint8_t *)k - (uint8_t *)bus->kdbus_buffer;
- ioctl(bus->input_fd, KDBUS_CMD_FREE, &off);
-
- KDBUS_ITEM_FOREACH(d, k, items) {
-
- if (d->type == KDBUS_ITEM_FDS)
- close_many(d->fds, (d->size - offsetof(struct kdbus_item, fds)) / sizeof(int));
- else if (d->type == KDBUS_ITEM_PAYLOAD_MEMFD)
- close_nointr_nofail(d->memfd.fd);
- }
-}
-
-static int push_name_owner_changed(sd_bus *bus, const char *name, const char *old_owner, const char *new_owner) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert(bus);
-
- r = sd_bus_message_new_signal(
- bus,
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "NameOwnerChanged",
- &m);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "sss", name, old_owner, new_owner);
- if (r < 0)
- return r;
-
- m->sender = "org.freedesktop.DBus";
-
- r = bus_seal_synthetic_message(bus, m);
- if (r < 0)
- return r;
-
- bus->rqueue[bus->rqueue_size++] = m;
- m = NULL;
-
- return 1;
-}
-
-static int translate_name_change(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) {
- char new_owner[UNIQUE_NAME_MAX], old_owner[UNIQUE_NAME_MAX];
-
- assert(bus);
- assert(k);
- assert(d);
-
- if (d->type == KDBUS_ITEM_NAME_ADD || (d->name_change.old.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR)))
- old_owner[0] = 0;
- else
- sprintf(old_owner, ":1.%llu", (unsigned long long) d->name_change.old.id);
-
- if (d->type == KDBUS_ITEM_NAME_REMOVE || (d->name_change.new.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))) {
-
- if (isempty(old_owner))
- return 0;
-
- new_owner[0] = 0;
- } else
- sprintf(new_owner, ":1.%llu", (unsigned long long) d->name_change.new.id);
-
- return push_name_owner_changed(bus, d->name_change.name, old_owner, new_owner);
-}
-
-static int translate_id_change(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) {
- char owner[UNIQUE_NAME_MAX];
-
- assert(bus);
- assert(k);
- assert(d);
-
- sprintf(owner, ":1.%llu", d->id_change.id);
-
- return push_name_owner_changed(
- bus, owner,
- d->type == KDBUS_ITEM_ID_ADD ? NULL : owner,
- d->type == KDBUS_ITEM_ID_ADD ? owner : NULL);
-}
-
-static int translate_reply(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert(bus);
- assert(k);
- assert(d);
-
- r = bus_message_new_synthetic_error(
- bus,
- k->cookie_reply,
- d->type == KDBUS_ITEM_REPLY_TIMEOUT ?
- &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call timed out") :
- &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call peer died"),
- &m);
- if (r < 0)
- return r;
-
- m->sender = "org.freedesktop.DBus";
-
- r = bus_seal_synthetic_message(bus, m);
- if (r < 0)
- return r;
-
- bus->rqueue[bus->rqueue_size++] = m;
- m = NULL;
-
- return 1;
-}
-
-static int bus_kernel_translate_message(sd_bus *bus, struct kdbus_msg *k) {
- struct kdbus_item *d, *found = NULL;
-
- static int (* const translate[])(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) = {
- [KDBUS_ITEM_NAME_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change,
- [KDBUS_ITEM_NAME_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change,
- [KDBUS_ITEM_NAME_CHANGE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change,
-
- [KDBUS_ITEM_ID_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change,
- [KDBUS_ITEM_ID_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change,
-
- [KDBUS_ITEM_REPLY_TIMEOUT - _KDBUS_ITEM_KERNEL_BASE] = translate_reply,
- [KDBUS_ITEM_REPLY_DEAD - _KDBUS_ITEM_KERNEL_BASE] = translate_reply,
- };
-
- assert(bus);
- assert(k);
- assert(k->payload_type == KDBUS_PAYLOAD_KERNEL);
-
- KDBUS_ITEM_FOREACH(d, k, items) {
- if (d->type >= _KDBUS_ITEM_KERNEL_BASE && d->type < _KDBUS_ITEM_KERNEL_BASE + ELEMENTSOF(translate)) {
- if (found)
- return -EBADMSG;
- found = d;
- } else
- log_debug("Got unknown field from kernel %llu", d->type);
- }
-
- if (!found) {
- log_debug("Didn't find a kernel message to translate.");
- return 0;
- }
-
- return translate[found->type - _KDBUS_ITEM_KERNEL_BASE](bus, k, found);
-}
-
-int bus_kernel_read_message(sd_bus *bus) {
- struct kdbus_cmd_recv recv = {};
- struct kdbus_msg *k;
- int r;
-
- assert(bus);
-
- r = bus_rqueue_make_room(bus);
- if (r < 0)
- return r;
-
- r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, &recv);
- if (r < 0) {
- if (errno == EAGAIN)
- return 0;
-
- return -errno;
- }
- k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + recv.offset);
-
- if (k->payload_type == KDBUS_PAYLOAD_DBUS) {
- r = bus_kernel_make_message(bus, k);
-
- /* Anybody can send us invalid messages, let's just drop them. */
- if (r == -EBADMSG || r == -EPROTOTYPE) {
- log_debug("Ignoring invalid message: %s", strerror(-r));
- r = 0;
- }
-
- } else if (k->payload_type == KDBUS_PAYLOAD_KERNEL)
- r = bus_kernel_translate_message(bus, k);
- else
- r = 0;
-
- if (r <= 0)
- close_kdbus_msg(bus, k);
-
- return r < 0 ? r : 1;
-}
-
-int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *allocated) {
- struct memfd_cache *c;
- int fd;
-
- assert(address);
- assert(mapped);
- assert(allocated);
-
- if (!bus || !bus->is_kernel)
- return -ENOTSUP;
-
- assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) >= 0);
-
- if (bus->n_memfd_cache <= 0) {
- struct kdbus_cmd_memfd_make cmd = {
- .size = sizeof(struct kdbus_cmd_memfd_make),
- };
- int r;
-
- assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
-
- r = ioctl(bus->input_fd, KDBUS_CMD_MEMFD_NEW, &cmd);
- if (r < 0)
- return -errno;
-
- *address = NULL;
- *mapped = 0;
- *allocated = 0;
- return cmd.fd;
- }
-
- c = &bus->memfd_cache[--bus->n_memfd_cache];
-
- assert(c->fd >= 0);
- assert(c->mapped == 0 || c->address);
-
- *address = c->address;
- *mapped = c->mapped;
- *allocated = c->allocated;
- fd = c->fd;
-
- assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
-
- return fd;
-}
-
-static void close_and_munmap(int fd, void *address, size_t size) {
- if (size > 0)
- assert_se(munmap(address, PAGE_ALIGN(size)) >= 0);
-
- close_nointr_nofail(fd);
-}
-
-void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t mapped, size_t allocated) {
- struct memfd_cache *c;
- uint64_t max_mapped = PAGE_ALIGN(MEMFD_CACHE_ITEM_SIZE_MAX);
-
- assert(fd >= 0);
- assert(mapped == 0 || address);
-
- if (!bus || !bus->is_kernel) {
- close_and_munmap(fd, address, mapped);
- return;
- }
-
- assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) >= 0);
-
- if (bus->n_memfd_cache >= ELEMENTSOF(bus->memfd_cache)) {
- assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
-
- close_and_munmap(fd, address, mapped);
- return;
- }
-
- c = &bus->memfd_cache[bus->n_memfd_cache++];
- c->fd = fd;
- c->address = address;
-
- /* If overly long, let's return a bit to the OS */
- if (mapped > max_mapped) {
- assert_se(ioctl(fd, KDBUS_CMD_MEMFD_SIZE_SET, &max_mapped) >= 0);
- assert_se(munmap((uint8_t*) address + max_mapped, PAGE_ALIGN(mapped - max_mapped)) >= 0);
- c->mapped = c->allocated = max_mapped;
- } else {
- c->mapped = mapped;
- c->allocated = allocated;
- }
-
- assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
-}
-
-void bus_kernel_flush_memfd(sd_bus *b) {
- unsigned i;
-
- assert(b);
-
- for (i = 0; i < b->n_memfd_cache; i++)
- close_and_munmap(b->memfd_cache[i].fd, b->memfd_cache[i].address, b->memfd_cache[i].mapped);
-}
-
-int kdbus_translate_request_name_flags(uint64_t flags, uint64_t *kdbus_flags) {
- uint64_t f = 0;
-
- assert(kdbus_flags);
-
- if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT)
- f |= KDBUS_NAME_ALLOW_REPLACEMENT;
-
- if (flags & SD_BUS_NAME_REPLACE_EXISTING)
- f |= KDBUS_NAME_REPLACE_EXISTING;
-
- if (flags & SD_BUS_NAME_QUEUE)
- f |= KDBUS_NAME_QUEUE;
-
- *kdbus_flags = f;
- return 0;
-}
-
-int kdbus_translate_attach_flags(uint64_t mask, uint64_t *kdbus_mask) {
- uint64_t m = 0;
-
- assert(kdbus_mask);
-
- if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID))
- m |= KDBUS_ATTACH_CREDS;
-
- if (mask & (SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM))
- m |= KDBUS_ATTACH_COMM;
-
- if (mask & SD_BUS_CREDS_EXE)
- m |= KDBUS_ATTACH_EXE;
-
- if (mask & SD_BUS_CREDS_CMDLINE)
- m |= KDBUS_ATTACH_CMDLINE;
-
- if (mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID))
- m |= KDBUS_ATTACH_CGROUP;
-
- if (mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS))
- m |= KDBUS_ATTACH_CAPS;
-
- if (mask & SD_BUS_CREDS_SELINUX_CONTEXT)
- m |= KDBUS_ATTACH_SECLABEL;
-
- if (mask & (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))
- m |= KDBUS_ATTACH_AUDIT;
-
- if (mask & SD_BUS_CREDS_WELL_KNOWN_NAMES)
- m |= KDBUS_ATTACH_NAMES;
-
- *kdbus_mask = m;
- return 0;
-}
-
-int bus_kernel_create_bus(const char *name, bool world, char **s) {
- struct kdbus_cmd_make *make;
- struct kdbus_item *n;
- int fd;
-
- assert(name);
- assert(s);
-
- fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- make = alloca0(ALIGN8(offsetof(struct kdbus_cmd_make, items) +
- offsetof(struct kdbus_item, data64) + sizeof(uint64_t) +
- offsetof(struct kdbus_item, str) +
- DECIMAL_STR_MAX(uid_t) + 1 + strlen(name) + 1));
-
- make->size = offsetof(struct kdbus_cmd_make, items);
-
- n = make->items;
- n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t);
- n->type = KDBUS_ITEM_BLOOM_SIZE;
- n->data64[0] = BLOOM_SIZE;
- assert_cc(BLOOM_SIZE % 8 == 0);
- make->size += ALIGN8(n->size);
-
- n = KDBUS_ITEM_NEXT(n);
- sprintf(n->str, "%lu-%s", (unsigned long) getuid(), name);
- n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1;
- n->type = KDBUS_ITEM_MAKE_NAME;
- make->size += ALIGN8(n->size);
-
- make->flags = KDBUS_MAKE_POLICY_OPEN | (world ? KDBUS_MAKE_ACCESS_WORLD : 0);
-
- if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) {
- close_nointr_nofail(fd);
- return -errno;
- }
-
- /* The higher 32bit of the flags field are considered
- * 'incompatible flags'. Refuse them all for now. */
- if (make->flags > 0xFFFFFFFFULL) {
- close_nointr_nofail(fd);
- return -ENOTSUP;
- }
-
- if (s) {
- char *p;
-
- p = strjoin("/dev/kdbus/", n->str, "/bus", NULL);
- if (!p) {
- close_nointr_nofail(fd);
- return -ENOMEM;
- }
-
- *s = p;
- }
-
- return fd;
-}
-
-int bus_kernel_create_starter(const char *bus, const char *name) {
- struct kdbus_cmd_hello *hello;
- struct kdbus_item *n;
- char *p;
- int fd;
-
- assert(bus);
- assert(name);
-
- p = alloca(sizeof("/dev/kdbus/") - 1 + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + sizeof("/bus"));
- sprintf(p, "/dev/kdbus/%lu-%s/bus", (unsigned long) getuid(), bus);
-
- fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- hello = alloca0(ALIGN8(offsetof(struct kdbus_cmd_hello, items) +
- offsetof(struct kdbus_item, str) +
- strlen(name) + 1));
-
- n = hello->items;
- strcpy(n->str, name);
- n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1;
- n->type = KDBUS_ITEM_NAME;
-
- hello->size = ALIGN8(offsetof(struct kdbus_cmd_hello, items) + n->size);
- hello->conn_flags = KDBUS_HELLO_ACTIVATOR;
- hello->pool_size = KDBUS_POOL_SIZE;
-
- if (ioctl(fd, KDBUS_CMD_HELLO, hello) < 0) {
- close_nointr_nofail(fd);
- return -errno;
- }
-
- /* The higher 32bit of both flags fields are considered
- * 'incompatible flags'. Refuse them all for now. */
- if (hello->bus_flags > 0xFFFFFFFFULL ||
- hello->conn_flags > 0xFFFFFFFFULL) {
- close_nointr_nofail(fd);
- return -ENOTSUP;
- }
-
- if (hello->bloom_size != BLOOM_SIZE) {
- close_nointr_nofail(fd);
- return -ENOTSUP;
- }
-
- return fd;
-}
-
-int bus_kernel_create_namespace(const char *name, char **s) {
- struct kdbus_cmd_make *make;
- struct kdbus_item *n;
- int fd;
-
- assert(name);
- assert(s);
-
- fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- make = alloca0(ALIGN8(offsetof(struct kdbus_cmd_make, items) +
- offsetof(struct kdbus_item, str) +
- strlen(name) + 1));
-
- n = make->items;
- strcpy(n->str, name);
- n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1;
- n->type = KDBUS_ITEM_MAKE_NAME;
-
- make->size = ALIGN8(offsetof(struct kdbus_cmd_make, items) + n->size);
- make->flags = KDBUS_MAKE_POLICY_OPEN | KDBUS_MAKE_ACCESS_WORLD;
-
- if (ioctl(fd, KDBUS_CMD_NS_MAKE, make) < 0) {
- close_nointr_nofail(fd);
- return -errno;
- }
-
- /* The higher 32bit of the flags field are considered
- * 'incompatible flags'. Refuse them all for now. */
- if (make->flags > 0xFFFFFFFFULL) {
- close_nointr_nofail(fd);
- return -ENOTSUP;
- }
-
- if (s) {
- char *p;
-
- p = strappend("/dev/kdbus/ns/", name);
- if (!p) {
- close_nointr_nofail(fd);
- return -ENOMEM;
- }
-
- *s = p;
- }
-
- return fd;
-}
-
-int bus_kernel_create_monitor(const char *bus) {
- struct kdbus_cmd_hello *hello;
- char *p;
- int fd;
-
- assert(bus);
-
- p = alloca(sizeof("/dev/kdbus/") - 1 + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + sizeof("/bus"));
- sprintf(p, "/dev/kdbus/%lu-%s/bus", (unsigned long) getuid(), bus);
-
- fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- hello = alloca0(sizeof(struct kdbus_cmd_hello));
- hello->size = sizeof(struct kdbus_cmd_hello);
- hello->conn_flags = KDBUS_HELLO_ACTIVATOR;
- hello->pool_size = KDBUS_POOL_SIZE;
-
- if (ioctl(fd, KDBUS_CMD_HELLO, hello) < 0) {
- close_nointr_nofail(fd);
- return -errno;
- }
-
- /* The higher 32bit of both flags fields are considered
- * 'incompatible flags'. Refuse them all for now. */
- if (hello->bus_flags > 0xFFFFFFFFULL ||
- hello->conn_flags > 0xFFFFFFFFULL) {
- close_nointr_nofail(fd);
- return -ENOTSUP;
- }
-
- return fd;
-}
-
-int bus_kernel_try_close(sd_bus *bus) {
- assert(bus);
- assert(bus->is_kernel);
-
- if (ioctl(bus->input_fd, KDBUS_CMD_BYEBYE) < 0)
- return -errno;
-
- return 0;
-}
diff --git a/src/libsystemd/bus-kernel.h b/src/libsystemd/bus-kernel.h
deleted file mode 100644
index 2aba0bb..0000000
--- a/src/libsystemd/bus-kernel.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-
-#include "sd-bus.h"
-
-#define KDBUS_ITEM_NEXT(item) \
- (typeof(item))(((uint8_t *)item) + ALIGN8((item)->size))
-
-#define KDBUS_ITEM_FOREACH(part, head, first) \
- for (part = (head)->first; \
- (uint8_t *)(part) < (uint8_t *)(head) + (head)->size; \
- part = KDBUS_ITEM_NEXT(part))
-
-#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data)
-#define KDBUS_ITEM_SIZE(s) ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE)
-
-#define MEMFD_CACHE_MAX 32
-
-/* When we cache a memfd block for reuse, we will truncate blocks
- * longer than this in order not to keep too much data around. */
-#define MEMFD_CACHE_ITEM_SIZE_MAX (128*1024)
-
-/* This determines at which minimum size we prefer sending memfds over
- * sending vectors */
-#define MEMFD_MIN_SIZE (128*1024)
-
-/* The size of the per-connection memory pool that we set up and where
- * the kernel places our incoming messages */
-#define KDBUS_POOL_SIZE (16*1024*1024)
-
-struct memfd_cache {
- int fd;
- void *address;
- size_t mapped;
- size_t allocated;
-};
-
-int bus_kernel_connect(sd_bus *b);
-int bus_kernel_take_fd(sd_bus *b);
-
-int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m);
-int bus_kernel_read_message(sd_bus *bus);
-
-int bus_kernel_create_bus(const char *name, bool world, char **s);
-int bus_kernel_create_namespace(const char *name, char **s);
-int bus_kernel_create_starter(const char *bus, const char *name);
-int bus_kernel_create_monitor(const char *bus);
-
-int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *allocated);
-void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t mapped, size_t allocated);
-
-void bus_kernel_flush_memfd(sd_bus *bus);
-
-int bus_kernel_parse_unique_name(const char *s, uint64_t *id);
-
-int kdbus_translate_request_name_flags(uint64_t sd_bus_flags, uint64_t *kdbus_flags);
-int kdbus_translate_attach_flags(uint64_t sd_bus_flags, uint64_t *kdbus_flags);
-
-int bus_kernel_try_close(sd_bus *bus);
diff --git a/src/libsystemd/bus-match.c b/src/libsystemd/bus-match.c
deleted file mode 100644
index ffc9756..0000000
--- a/src/libsystemd/bus-match.c
+++ /dev/null
@@ -1,1078 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "bus-internal.h"
-#include "bus-message.h"
-#include "bus-match.h"
-#include "bus-error.h"
-#include "bus-util.h"
-#include "strv.h"
-
-/* Example:
- *
- * A: type=signal,sender=foo,interface=bar
- * B: type=signal,sender=quux,interface=fips
- * C: type=signal,sender=quux,interface=waldo
- * D: type=signal,member=test
- * E: sender=miau
- * F: type=signal
- * G: type=signal
- *
- * results in this tree:
- *
- * BUS_MATCH_ROOT
- * + BUS_MATCH_MESSAGE_TYPE
- * | ` BUS_MATCH_VALUE: value == signal
- * | + DBUS_MATCH_SENDER
- * | | + BUS_MATCH_VALUE: value == foo
- * | | | ` DBUS_MATCH_INTERFACE
- * | | | ` BUS_MATCH_VALUE: value == bar
- * | | | ` BUS_MATCH_LEAF: A
- * | | ` BUS_MATCH_VALUE: value == quux
- * | | ` DBUS_MATCH_INTERFACE
- * | | | BUS_MATCH_VALUE: value == fips
- * | | | ` BUS_MATCH_LEAF: B
- * | | ` BUS_MATCH_VALUE: value == waldo
- * | | ` BUS_MATCH_LEAF: C
- * | + DBUS_MATCH_MEMBER
- * | | ` BUS_MATCH_VALUE: value == test
- * | | ` BUS_MATCH_LEAF: D
- * | + BUS_MATCH_LEAF: F
- * | ` BUS_MATCH_LEAF: G
- * ` BUS_MATCH_SENDER
- * ` BUS_MATCH_VALUE: value == miau
- * ` BUS_MATCH_LEAF: E
- */
-
-static inline bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) {
- return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_NAMESPACE_LAST;
-}
-
-static inline bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) {
- return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) ||
- (t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST);
-}
-
-static void bus_match_node_free(struct bus_match_node *node) {
- assert(node);
- assert(node->parent);
- assert(!node->child);
- assert(node->type != BUS_MATCH_ROOT);
- assert(node->type < _BUS_MATCH_NODE_TYPE_MAX);
-
- if (node->parent->child) {
- /* We are apparently linked into the parent's child
- * list. Let's remove us from there. */
- if (node->prev) {
- assert(node->prev->next == node);
- node->prev->next = node->next;
- } else {
- assert(node->parent->child == node);
- node->parent->child = node->next;
- }
-
- if (node->next)
- node->next->prev = node->prev;
- }
-
- if (node->type == BUS_MATCH_VALUE) {
- /* We might be in the parent's hash table, so clean
- * this up */
-
- if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
- hashmap_remove(node->parent->compare.children, UINT_TO_PTR(node->value.u8));
- else if (BUS_MATCH_CAN_HASH(node->parent->type) && node->value.str)
- hashmap_remove(node->parent->compare.children, node->value.str);
-
- free(node->value.str);
- }
-
- if (BUS_MATCH_IS_COMPARE(node->type)) {
- assert(hashmap_isempty(node->compare.children));
- hashmap_free(node->compare.children);
- }
-
- free(node);
-}
-
-static bool bus_match_node_maybe_free(struct bus_match_node *node) {
- assert(node);
-
- if (node->child)
- return false;
-
- if (BUS_MATCH_IS_COMPARE(node->type) && !hashmap_isempty(node->compare.children))
- return true;
-
- bus_match_node_free(node);
- return true;
-}
-
-static bool value_node_test(
- struct bus_match_node *node,
- enum bus_match_node_type parent_type,
- uint8_t value_u8,
- const char *value_str,
- sd_bus_message *m) {
-
- assert(node);
- assert(node->type == BUS_MATCH_VALUE);
-
- /* Tests parameters against this value node, doing prefix
- * magic and stuff. */
-
- switch (parent_type) {
-
- case BUS_MATCH_MESSAGE_TYPE:
- return node->value.u8 == value_u8;
-
- case BUS_MATCH_SENDER:
- if (streq_ptr(node->value.str, value_str))
- return true;
-
- if (m->creds.mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
- char **i;
-
- /* on kdbus we have the well known names list
- * in the credentials, let's make use of that
- * for an accurate match */
-
- STRV_FOREACH(i, m->creds.well_known_names)
- if (streq_ptr(node->value.str, *i))
- return true;
-
- } else {
-
- /* If we don't have kdbus, we don't know the
- * well-known names of the senders. In that,
- * let's just hope that dbus-daemon doesn't
- * send us stuff we didn't want. */
-
- if (node->value.str[0] != ':' && value_str && value_str[0] == ':')
- return true;
- }
-
- return false;
-
- case BUS_MATCH_DESTINATION:
- case BUS_MATCH_INTERFACE:
- case BUS_MATCH_MEMBER:
- case BUS_MATCH_PATH:
- case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
- return streq_ptr(node->value.str, value_str);
-
- case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
- return namespace_simple_pattern(node->value.str, value_str);
-
- case BUS_MATCH_PATH_NAMESPACE:
- return path_simple_pattern(node->value.str, value_str);
-
- case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
- return path_complex_pattern(node->value.str, value_str);
-
- default:
- assert_not_reached("Invalid node type");
- }
-}
-
-static bool value_node_same(
- struct bus_match_node *node,
- enum bus_match_node_type parent_type,
- uint8_t value_u8,
- const char *value_str) {
-
- /* Tests parameters against this value node, not doing prefix
- * magic and stuff, i.e. this one actually compares the match
- * itself.*/
-
- assert(node);
- assert(node->type == BUS_MATCH_VALUE);
-
- switch (parent_type) {
-
- case BUS_MATCH_MESSAGE_TYPE:
- return node->value.u8 == value_u8;
-
- case BUS_MATCH_SENDER:
- case BUS_MATCH_DESTINATION:
- case BUS_MATCH_INTERFACE:
- case BUS_MATCH_MEMBER:
- case BUS_MATCH_PATH:
- case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
- case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
- case BUS_MATCH_PATH_NAMESPACE:
- case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
- return streq(node->value.str, value_str);
-
- default:
- assert_not_reached("Invalid node type");
- }
-}
-
-int bus_match_run(
- sd_bus *bus,
- struct bus_match_node *node,
- sd_bus_message *m) {
-
-
- const char *test_str = NULL;
- uint8_t test_u8 = 0;
- int r;
-
- assert(m);
-
- if (!node)
- return 0;
-
- if (bus && bus->match_callbacks_modified)
- return 0;
-
- /* Not these special semantics: when traversing the tree we
- * usually let bus_match_run() when called for a node
- * recursively invoke bus_match_run(). There's are two
- * exceptions here though, which are BUS_NODE_ROOT (which
- * cannot have a sibling), and BUS_NODE_VALUE (whose siblings
- * are invoked anyway by its parent. */
-
- switch (node->type) {
-
- case BUS_MATCH_ROOT:
-
- /* Run all children. Since we cannot have any siblings
- * we won't call any. The children of the root node
- * are compares or leaves, they will automatically
- * call their siblings. */
- return bus_match_run(bus, node->child, m);
-
- case BUS_MATCH_VALUE:
-
- /* Run all children. We don't execute any siblings, we
- * assume our caller does that. The children of value
- * nodes are compares or leaves, they will
- * automatically call their siblings */
-
- assert(node->child);
- return bus_match_run(bus, node->child, m);
-
- case BUS_MATCH_LEAF:
-
- if (bus) {
- if (node->leaf.last_iteration == bus->iteration_counter)
- return 0;
-
- node->leaf.last_iteration = bus->iteration_counter;
- }
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- /* Run the callback. And then invoke siblings. */
- if (node->leaf.callback) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
-
- r = node->leaf.callback(bus, m, node->leaf.userdata, &error_buffer);
- r = bus_maybe_reply_error(m, r, &error_buffer);
- if (r != 0)
- return r;
- }
-
- return bus_match_run(bus, node->next, m);
-
- case BUS_MATCH_MESSAGE_TYPE:
- test_u8 = m->header->type;
- break;
-
- case BUS_MATCH_SENDER:
- test_str = m->sender;
- /* FIXME: resolve test_str from a well-known to a unique name first */
- break;
-
- case BUS_MATCH_DESTINATION:
- test_str = m->destination;
- break;
-
- case BUS_MATCH_INTERFACE:
- test_str = m->interface;
- break;
-
- case BUS_MATCH_MEMBER:
- test_str = m->member;
- break;
-
- case BUS_MATCH_PATH:
- case BUS_MATCH_PATH_NAMESPACE:
- test_str = m->path;
- break;
-
- case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
- test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG);
- break;
-
- case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
- test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH);
- break;
-
- case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
- test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE);
- break;
-
- default:
- assert_not_reached("Unknown match type.");
- }
-
- if (BUS_MATCH_CAN_HASH(node->type)) {
- struct bus_match_node *found;
-
- /* Lookup via hash table, nice! So let's jump directly. */
-
- if (test_str)
- found = hashmap_get(node->compare.children, test_str);
- else if (node->type == BUS_MATCH_MESSAGE_TYPE)
- found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8));
- else
- found = NULL;
-
- if (found) {
- r = bus_match_run(bus, found, m);
- if (r != 0)
- return r;
- }
- } else {
- struct bus_match_node *c;
-
- /* No hash table, so let's iterate manually... */
-
- for (c = node->child; c; c = c->next) {
- if (!value_node_test(c, node->type, test_u8, test_str, m))
- continue;
-
- r = bus_match_run(bus, c, m);
- if (r != 0)
- return r;
- }
- }
-
- if (bus && bus->match_callbacks_modified)
- return 0;
-
- /* And now, let's invoke our siblings */
- return bus_match_run(bus, node->next, m);
-}
-
-static int bus_match_add_compare_value(
- struct bus_match_node *where,
- enum bus_match_node_type t,
- uint8_t value_u8,
- const char *value_str,
- struct bus_match_node **ret) {
-
- struct bus_match_node *c = NULL, *n = NULL;
- int r;
-
- assert(where);
- assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
- assert(BUS_MATCH_IS_COMPARE(t));
- assert(ret);
-
- for (c = where->child; c && c->type != t; c = c->next)
- ;
-
- if (c) {
- /* Comparison node already exists? Then let's see if
- * the value node exists too. */
-
- if (t == BUS_MATCH_MESSAGE_TYPE)
- n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
- else if (BUS_MATCH_CAN_HASH(t))
- n = hashmap_get(c->compare.children, value_str);
- else {
- for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
- ;
- }
-
- if (n) {
- *ret = n;
- return 0;
- }
- } else {
- /* Comparison node, doesn't exist yet? Then let's
- * create it. */
-
- c = new0(struct bus_match_node, 1);
- if (!c) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->type = t;
- c->parent = where;
- c->next = where->child;
- if (c->next)
- c->next->prev = c;
- where->child = c;
-
- if (t == BUS_MATCH_MESSAGE_TYPE) {
- c->compare.children = hashmap_new(trivial_hash_func, trivial_compare_func);
- if (!c->compare.children) {
- r = -ENOMEM;
- goto fail;
- }
- } else if (BUS_MATCH_CAN_HASH(t)) {
- c->compare.children = hashmap_new(string_hash_func, string_compare_func);
- if (!c->compare.children) {
- r = -ENOMEM;
- goto fail;
- }
- }
- }
-
- n = new0(struct bus_match_node, 1);
- if (!n) {
- r = -ENOMEM;
- goto fail;
- }
-
- n->type = BUS_MATCH_VALUE;
- n->value.u8 = value_u8;
- if (value_str) {
- n->value.str = strdup(value_str);
- if (!n->value.str) {
- r = -ENOMEM;
- goto fail;
- }
- }
-
- n->parent = c;
- if (c->compare.children) {
-
- if (t == BUS_MATCH_MESSAGE_TYPE)
- r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n);
- else
- r = hashmap_put(c->compare.children, n->value.str, n);
-
- if (r < 0)
- goto fail;
- } else {
- n->next = c->child;
- if (n->next)
- n->next->prev = n;
- c->child = n;
- }
-
- *ret = n;
- return 1;
-
-fail:
- if (c)
- bus_match_node_maybe_free(c);
-
- if (n) {
- free(n->value.str);
- free(n);
- }
-
- return r;
-}
-
-static int bus_match_find_compare_value(
- struct bus_match_node *where,
- enum bus_match_node_type t,
- uint8_t value_u8,
- const char *value_str,
- struct bus_match_node **ret) {
-
- struct bus_match_node *c, *n;
-
- assert(where);
- assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
- assert(BUS_MATCH_IS_COMPARE(t));
- assert(ret);
-
- for (c = where->child; c && c->type != t; c = c->next)
- ;
-
- if (!c)
- return 0;
-
- if (t == BUS_MATCH_MESSAGE_TYPE)
- n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
- else if (BUS_MATCH_CAN_HASH(t))
- n = hashmap_get(c->compare.children, value_str);
- else {
- for (n = c->child; !value_node_same(n, t, value_u8, value_str); n = n->next)
- ;
- }
-
- if (n) {
- *ret = n;
- return 1;
- }
-
- return 0;
-}
-
-static int bus_match_add_leaf(
- struct bus_match_node *where,
- sd_bus_message_handler_t callback,
- void *userdata,
- uint64_t cookie,
- struct bus_match_node **ret) {
-
- struct bus_match_node *n;
-
- assert(where);
- assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
- assert(ret);
-
- n = new0(struct bus_match_node, 1);
- if (!n)
- return -ENOMEM;
-
- n->type = BUS_MATCH_LEAF;
- n->parent = where;
- n->next = where->child;
- if (n->next)
- n->next->prev = n;
- n->leaf.callback = callback;
- n->leaf.userdata = userdata;
- n->leaf.cookie = cookie;
-
- where->child = n;
-
- *ret = n;
- return 1;
-}
-
-static int bus_match_find_leaf(
- struct bus_match_node *where,
- sd_bus_message_handler_t callback,
- void *userdata,
- struct bus_match_node **ret) {
-
- struct bus_match_node *c;
-
- assert(where);
- assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
- assert(ret);
-
- for (c = where->child; c; c = c->next) {
- if (c->type == BUS_MATCH_LEAF &&
- c->leaf.callback == callback &&
- c->leaf.userdata == userdata) {
- *ret = c;
- return 1;
- }
- }
-
- return 0;
-}
-
-enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) {
- assert(k);
-
- if (n == 4 && startswith(k, "type"))
- return BUS_MATCH_MESSAGE_TYPE;
- if (n == 6 && startswith(k, "sender"))
- return BUS_MATCH_SENDER;
- if (n == 11 && startswith(k, "destination"))
- return BUS_MATCH_DESTINATION;
- if (n == 9 && startswith(k, "interface"))
- return BUS_MATCH_INTERFACE;
- if (n == 6 && startswith(k, "member"))
- return BUS_MATCH_MEMBER;
- if (n == 4 && startswith(k, "path"))
- return BUS_MATCH_PATH;
- if (n == 14 && startswith(k, "path_namespace"))
- return BUS_MATCH_PATH_NAMESPACE;
-
- if (n == 4 && startswith(k, "arg")) {
- int j;
-
- j = undecchar(k[3]);
- if (j < 0)
- return -EINVAL;
-
- return BUS_MATCH_ARG + j;
- }
-
- if (n == 5 && startswith(k, "arg")) {
- int a, b;
- enum bus_match_node_type t;
-
- a = undecchar(k[3]);
- b = undecchar(k[4]);
- if (a <= 0 || b < 0)
- return -EINVAL;
-
- t = BUS_MATCH_ARG + a * 10 + b;
- if (t > BUS_MATCH_ARG_LAST)
- return -EINVAL;
-
- return t;
- }
-
- if (n == 8 && startswith(k, "arg") && startswith(k + 4, "path")) {
- int j;
-
- j = undecchar(k[3]);
- if (j < 0)
- return -EINVAL;
-
- return BUS_MATCH_ARG_PATH + j;
- }
-
- if (n == 9 && startswith(k, "arg") && startswith(k + 5, "path")) {
- enum bus_match_node_type t;
- int a, b;
-
- a = undecchar(k[3]);
- b = undecchar(k[4]);
- if (a <= 0 || b < 0)
- return -EINVAL;
-
- t = BUS_MATCH_ARG_PATH + a * 10 + b;
- if (t > BUS_MATCH_ARG_PATH_LAST)
- return -EINVAL;
-
- return t;
- }
-
- if (n == 13 && startswith(k, "arg") && startswith(k + 4, "namespace")) {
- int j;
-
- j = undecchar(k[3]);
- if (j < 0)
- return -EINVAL;
-
- return BUS_MATCH_ARG_NAMESPACE + j;
- }
-
- if (n == 14 && startswith(k, "arg") && startswith(k + 5, "namespace")) {
- enum bus_match_node_type t;
- int a, b;
-
- a = undecchar(k[3]);
- b = undecchar(k[4]);
- if (a <= 0 || b < 0)
- return -EINVAL;
-
- t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b;
- if (t > BUS_MATCH_ARG_NAMESPACE_LAST)
- return -EINVAL;
-
- return t;
- }
-
- return -EINVAL;
-}
-
-static int match_component_compare(const void *a, const void *b) {
- const struct bus_match_component *x = a, *y = b;
-
- if (x->type < y->type)
- return -1;
- if (x->type > y->type)
- return 1;
-
- return 0;
-}
-
-void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) {
- unsigned i;
-
- for (i = 0; i < n_components; i++)
- free(components[i].value_str);
-
- free(components);
-}
-
-int bus_match_parse(
- const char *match,
- struct bus_match_component **_components,
- unsigned *_n_components) {
-
- const char *p = match;
- struct bus_match_component *components = NULL;
- size_t components_allocated = 0;
- unsigned n_components = 0, i;
- _cleanup_free_ char *value = NULL;
- int r;
-
- assert(match);
- assert(_components);
- assert(_n_components);
-
- while (*p != 0) {
- const char *eq, *q;
- enum bus_match_node_type t;
- unsigned j = 0;
- size_t value_allocated = 0;
- bool escaped = false, quoted;
- uint8_t u;
-
- eq = strchr(p, '=');
- if (!eq)
- return -EINVAL;
-
- t = bus_match_node_type_from_string(p, eq - p);
- if (t < 0)
- return -EINVAL;
-
- quoted = eq[1] == '\'';
-
- for (q = eq + 1 + quoted;; q++) {
-
- if (*q == 0) {
-
- if (quoted) {
- r = -EINVAL;
- goto fail;
- } else {
- if (value)
- value[j] = 0;
- break;
- }
- }
-
- if (!escaped) {
- if (*q == '\\') {
- escaped = true;
- continue;
- }
-
- if (quoted) {
- if (*q == '\'') {
- if (value)
- value[j] = 0;
- break;
- }
- } else {
- if (*q == ',') {
- if (value)
- value[j] = 0;
-
- break;
- }
- }
- }
-
- if (!GREEDY_REALLOC(value, value_allocated, j + 2)) {
- r = -ENOMEM;
- goto fail;
- }
-
- value[j++] = *q;
- escaped = false;
- }
-
- if (!value) {
- value = strdup("");
- if (!value) {
- r = -ENOMEM;
- goto fail;
- }
- }
-
- if (t == BUS_MATCH_MESSAGE_TYPE) {
- r = bus_message_type_from_string(value, &u);
- if (r < 0)
- goto fail;
-
- free(value);
- value = NULL;
- } else
- u = 0;
-
- if (!GREEDY_REALLOC(components, components_allocated, n_components + 1)) {
- r = -ENOMEM;
- goto fail;
- }
-
- components[n_components].type = t;
- components[n_components].value_str = value;
- components[n_components].value_u8 = u;
- n_components++;
-
- value = NULL;
-
- if (q[1] == 0)
- break;
-
- if (q[quoted] != ',') {
- r = -EINVAL;
- goto fail;
- }
-
- p = q + 1 + quoted;
- }
-
- /* Order the whole thing, so that we always generate the same tree */
- qsort_safe(components, n_components, sizeof(struct bus_match_component), match_component_compare);
-
- /* Check for duplicates */
- for (i = 0; i+1 < n_components; i++)
- if (components[i].type == components[i+1].type) {
- r = -EINVAL;
- goto fail;
- }
-
- *_components = components;
- *_n_components = n_components;
-
- return 0;
-
-fail:
- bus_match_parse_free(components, n_components);
- return r;
-}
-
-char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) {
- _cleanup_free_ FILE *f = NULL;
- char *buffer = NULL;
- size_t size = 0;
- unsigned i;
-
- if (n_components <= 0)
- return strdup("");
-
- assert(components);
-
- f = open_memstream(&buffer, &size);
- if (!f)
- return NULL;
-
- for (i = 0; i < n_components; i++) {
- char buf[32];
-
- if (i != 0)
- fputc(',', f);
-
- fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f);
- fputc('=', f);
- fputc('\'', f);
-
- if (components[i].type == BUS_MATCH_MESSAGE_TYPE)
- fputs(bus_message_type_to_string(components[i].value_u8), f);
- else
- fputs(components[i].value_str, f);
-
- fputc('\'', f);
- }
-
- fflush(f);
- if (ferror(f))
- return NULL;
-
- return buffer;
-}
-
-int bus_match_add(
- struct bus_match_node *root,
- struct bus_match_component *components,
- unsigned n_components,
- sd_bus_message_handler_t callback,
- void *userdata,
- uint64_t cookie,
- struct bus_match_node **ret) {
-
- unsigned i;
- struct bus_match_node *n;
- int r;
-
- assert(root);
-
- n = root;
- for (i = 0; i < n_components; i++) {
- r = bus_match_add_compare_value(
- n, components[i].type,
- components[i].value_u8, components[i].value_str, &n);
- if (r < 0)
- return r;
- }
-
- r = bus_match_add_leaf(n, callback, userdata, cookie, &n);
- if (r < 0)
- return r;
-
- if (ret)
- *ret = n;
-
- return 0;
-}
-
-int bus_match_remove(
- struct bus_match_node *root,
- struct bus_match_component *components,
- unsigned n_components,
- sd_bus_message_handler_t callback,
- void *userdata,
- uint64_t *cookie) {
-
- unsigned i;
- struct bus_match_node *n, **gc;
- int r;
-
- assert(root);
-
- gc = newa(struct bus_match_node*, n_components);
-
- n = root;
- for (i = 0; i < n_components; i++) {
- r = bus_match_find_compare_value(
- n, components[i].type,
- components[i].value_u8, components[i].value_str,
- &n);
- if (r <= 0)
- return r;
-
- gc[i] = n;
- }
-
- r = bus_match_find_leaf(n, callback, userdata, &n);
- if (r <= 0)
- return r;
-
- if (cookie)
- *cookie = n->leaf.cookie;
-
- /* Free the leaf */
- bus_match_node_free(n);
-
- /* Prune the tree above */
- for (i = n_components; i > 0; i --) {
- struct bus_match_node *p = gc[i-1]->parent;
-
- if (!bus_match_node_maybe_free(gc[i-1]))
- break;
-
- if (!bus_match_node_maybe_free(p))
- break;
- }
-
- return r;
-}
-
-void bus_match_free(struct bus_match_node *node) {
- struct bus_match_node *c;
-
- if (!node)
- return;
-
- if (BUS_MATCH_CAN_HASH(node->type)) {
- Iterator i;
-
- HASHMAP_FOREACH(c, node->compare.children, i)
- bus_match_free(c);
-
- assert(hashmap_isempty(node->compare.children));
- }
-
- while ((c = node->child))
- bus_match_free(c);
-
- if (node->type != BUS_MATCH_ROOT)
- bus_match_node_free(node);
-}
-
-const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) {
- switch (t) {
-
- case BUS_MATCH_ROOT:
- return "root";
-
- case BUS_MATCH_VALUE:
- return "value";
-
- case BUS_MATCH_LEAF:
- return "leaf";
-
- case BUS_MATCH_MESSAGE_TYPE:
- return "type";
-
- case BUS_MATCH_SENDER:
- return "sender";
-
- case BUS_MATCH_DESTINATION:
- return "destination";
-
- case BUS_MATCH_INTERFACE:
- return "interface";
-
- case BUS_MATCH_MEMBER:
- return "member";
-
- case BUS_MATCH_PATH:
- return "path";
-
- case BUS_MATCH_PATH_NAMESPACE:
- return "path_namespace";
-
- case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
- snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
- return buf;
-
- case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
- snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
- return buf;
-
- case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
- snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
- return buf;
-
- default:
- return NULL;
- }
-}
-
-void bus_match_dump(struct bus_match_node *node, unsigned level) {
- struct bus_match_node *c;
- _cleanup_free_ char *pfx = NULL;
- char buf[32];
-
- if (!node)
- return;
-
- pfx = strrep(" ", level);
- printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf)));
-
- if (node->type == BUS_MATCH_VALUE) {
- if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
- printf(" <%u>\n", node->value.u8);
- else
- printf(" <%s>\n", node->value.str);
- } else if (node->type == BUS_MATCH_ROOT)
- puts(" root");
- else if (node->type == BUS_MATCH_LEAF)
- printf(" %p/%p\n", node->leaf.callback, node->leaf.userdata);
- else
- putchar('\n');
-
- if (BUS_MATCH_CAN_HASH(node->type)) {
- Iterator i;
-
- HASHMAP_FOREACH(c, node->compare.children, i)
- bus_match_dump(c, level + 1);
- }
-
- for (c = node->child; c; c = c->next)
- bus_match_dump(c, level + 1);
-}
diff --git a/src/libsystemd/bus-match.h b/src/libsystemd/bus-match.h
deleted file mode 100644
index 056082b..0000000
--- a/src/libsystemd/bus-match.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "hashmap.h"
-
-#include "sd-bus.h"
-
-enum bus_match_node_type {
- BUS_MATCH_ROOT,
- BUS_MATCH_VALUE,
- BUS_MATCH_LEAF,
-
- /* The following are all different kinds of compare nodes */
- BUS_MATCH_SENDER,
- BUS_MATCH_MESSAGE_TYPE,
- BUS_MATCH_DESTINATION,
- BUS_MATCH_INTERFACE,
- BUS_MATCH_MEMBER,
- BUS_MATCH_PATH,
- BUS_MATCH_PATH_NAMESPACE,
- BUS_MATCH_ARG,
- BUS_MATCH_ARG_LAST = BUS_MATCH_ARG + 63,
- BUS_MATCH_ARG_PATH,
- BUS_MATCH_ARG_PATH_LAST = BUS_MATCH_ARG_PATH + 63,
- BUS_MATCH_ARG_NAMESPACE,
- BUS_MATCH_ARG_NAMESPACE_LAST = BUS_MATCH_ARG_NAMESPACE + 63,
- _BUS_MATCH_NODE_TYPE_MAX,
- _BUS_MATCH_NODE_TYPE_INVALID = -1
-};
-
-struct bus_match_node {
- enum bus_match_node_type type;
- struct bus_match_node *parent, *next, *prev, *child;
-
- union {
- struct {
- char *str;
- uint8_t u8;
- } value;
- struct {
- sd_bus_message_handler_t callback;
- void *userdata;
- unsigned last_iteration;
- uint64_t cookie;
- } leaf;
- struct {
- /* If this is set, then the child is NULL */
- Hashmap *children;
- } compare;
- };
-};
-
-struct bus_match_component {
- enum bus_match_node_type type;
- uint8_t value_u8;
- char *value_str;
-};
-
-int bus_match_run(sd_bus *bus, struct bus_match_node *root, sd_bus_message *m);
-
-int bus_match_add(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, sd_bus_message_handler_t callback, void *userdata, uint64_t cookie, struct bus_match_node **ret);
-int bus_match_remove(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, sd_bus_message_handler_t callback, void *userdata, uint64_t *cookie);
-
-void bus_match_free(struct bus_match_node *node);
-
-void bus_match_dump(struct bus_match_node *node, unsigned level);
-
-const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l);
-enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n);
-
-int bus_match_parse(const char *match, struct bus_match_component **_components, unsigned *_n_components);
-void bus_match_parse_free(struct bus_match_component *components, unsigned n_components);
-char *bus_match_to_string(struct bus_match_component *components, unsigned n_components);
diff --git a/src/libsystemd/bus-message.c b/src/libsystemd/bus-message.c
deleted file mode 100644
index 1a2039b..0000000
--- a/src/libsystemd/bus-message.c
+++ /dev/null
@@ -1,5535 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-
-#include "util.h"
-#include "utf8.h"
-#include "strv.h"
-#include "time-util.h"
-#include "cgroup-util.h"
-
-#include "sd-bus.h"
-#include "bus-message.h"
-#include "bus-internal.h"
-#include "bus-type.h"
-#include "bus-signature.h"
-#include "bus-gvariant.h"
-#include "bus-util.h"
-
-static int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored);
-
-static void *adjust_pointer(const void *p, void *old_base, size_t sz, void *new_base) {
-
- if (p == NULL)
- return NULL;
-
- if (old_base == new_base)
- return (void*) p;
-
- if ((uint8_t*) p < (uint8_t*) old_base)
- return (void*) p;
-
- if ((uint8_t*) p >= (uint8_t*) old_base + sz)
- return (void*) p;
-
- return (uint8_t*) new_base + ((uint8_t*) p - (uint8_t*) old_base);
-}
-
-static void message_free_part(sd_bus_message *m, struct bus_body_part *part) {
- assert(m);
- assert(part);
-
- if (part->memfd >= 0) {
- /* If we can reuse the memfd, try that. For that it
- * can't be sealed yet. */
-
- if (!part->sealed)
- bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped, part->allocated);
- else {
- if (part->mapped > 0)
- assert_se(munmap(part->data, part->mapped) == 0);
-
- close_nointr_nofail(part->memfd);
- }
-
- } else if (part->munmap_this)
- munmap(part->data, part->mapped);
- else if (part->free_this)
- free(part->data);
-
- if (part != &m->body)
- free(part);
-}
-
-static void message_reset_parts(sd_bus_message *m) {
- struct bus_body_part *part;
-
- assert(m);
-
- part = &m->body;
- while (m->n_body_parts > 0) {
- struct bus_body_part *next = part->next;
- message_free_part(m, part);
- part = next;
- m->n_body_parts--;
- }
-
- m->body_end = NULL;
-
- m->cached_rindex_part = NULL;
- m->cached_rindex_part_begin = 0;
-}
-
-static void message_reset_containers(sd_bus_message *m) {
- unsigned i;
-
- assert(m);
-
- for (i = 0; i < m->n_containers; i++) {
- free(m->containers[i].signature);
- free(m->containers[i].offsets);
- }
-
- free(m->containers);
- m->containers = NULL;
-
- m->n_containers = m->containers_allocated = 0;
- m->root_container.index = 0;
-}
-
-static void message_free(sd_bus_message *m) {
- assert(m);
-
- if (m->free_header)
- free(m->header);
-
- message_reset_parts(m);
-
- if (m->free_kdbus)
- free(m->kdbus);
-
- if (m->release_kdbus) {
- uint64_t off;
-
- off = (uint8_t *)m->kdbus - (uint8_t *)m->bus->kdbus_buffer;
- ioctl(m->bus->input_fd, KDBUS_CMD_FREE, &off);
- }
-
- if (m->bus)
- sd_bus_unref(m->bus);
-
- if (m->free_fds) {
- close_many(m->fds, m->n_fds);
- free(m->fds);
- }
-
- if (m->iovec != m->iovec_fixed)
- free(m->iovec);
-
- message_reset_containers(m);
- free(m->root_container.signature);
- free(m->root_container.offsets);
-
- free(m->root_container.peeked_signature);
-
- bus_creds_done(&m->creds);
- free(m);
-}
-
-static void *message_extend_fields(sd_bus_message *m, size_t align, size_t sz, bool add_offset) {
- void *op, *np;
- size_t old_size, new_size, start;
-
- assert(m);
-
- if (m->poisoned)
- return NULL;
-
- old_size = sizeof(struct bus_header) + m->header->fields_size;
- start = ALIGN_TO(old_size, align);
- new_size = start + sz;
-
- if (old_size == new_size)
- return (uint8_t*) m->header + old_size;
-
- if (new_size > (size_t) ((uint32_t) -1))
- goto poison;
-
- if (m->free_header) {
- np = realloc(m->header, ALIGN8(new_size));
- if (!np)
- goto poison;
- } else {
- /* Initially, the header is allocated as part of of
- * the sd_bus_message itself, let's replace it by
- * dynamic data */
-
- np = malloc(ALIGN8(new_size));
- if (!np)
- goto poison;
-
- memcpy(np, m->header, sizeof(struct bus_header));
- }
-
- /* Zero out padding */
- if (start > old_size)
- memset((uint8_t*) np + old_size, 0, start - old_size);
-
- op = m->header;
- m->header = np;
- m->header->fields_size = new_size - sizeof(struct bus_header);
-
- /* Adjust quick access pointers */
- m->path = adjust_pointer(m->path, op, old_size, m->header);
- m->interface = adjust_pointer(m->interface, op, old_size, m->header);
- m->member = adjust_pointer(m->member, op, old_size, m->header);
- m->destination = adjust_pointer(m->destination, op, old_size, m->header);
- m->sender = adjust_pointer(m->sender, op, old_size, m->header);
- m->error.name = adjust_pointer(m->error.name, op, old_size, m->header);
-
- m->free_header = true;
-
- if (add_offset) {
- if (m->n_header_offsets >= ELEMENTSOF(m->header_offsets))
- goto poison;
-
- m->header_offsets[m->n_header_offsets++] = new_size - sizeof(struct bus_header);
- }
-
- return (uint8_t*) np + start;
-
-poison:
- m->poisoned = true;
- return NULL;
-}
-
-static int message_append_field_string(
- sd_bus_message *m,
- uint8_t h,
- char type,
- const char *s,
- const char **ret) {
-
- size_t l;
- uint8_t *p;
-
- assert(m);
-
- /* dbus1 doesn't allow strings over 32bit, let's enforce this
- * globally, to not risk convertability */
- l = strlen(s);
- if (l > (size_t) (uint32_t) -1)
- return -EINVAL;
-
- /* Signature "(yv)" where the variant contains "s" */
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
-
- /* (field id byte + 7x padding, ((string + NUL) + NUL + signature string 's') */
- p = message_extend_fields(m, 8, 1 + 7 + l + 1 + 1 + 1, true);
- if (!p)
- return -ENOMEM;
-
- p[0] = h;
- memset(p+1, 0, 7);
- memcpy(p+8, s, l);
- p[8+l] = 0;
- p[8+l+1] = 0;
- p[8+l+2] = type;
-
- if (ret)
- *ret = (char*) p + 8;
-
- } else {
- /* (field id byte + (signature length + signature 's' + NUL) + (string length + string + NUL)) */
- p = message_extend_fields(m, 8, 4 + 4 + l + 1, false);
- if (!p)
- return -ENOMEM;
-
- p[0] = h;
- p[1] = 1;
- p[2] = type;
- p[3] = 0;
-
- ((uint32_t*) p)[1] = l;
- memcpy(p + 8, s, l + 1);
-
- if (ret)
- *ret = (char*) p + 8;
- }
-
- return 0;
-}
-
-static int message_append_field_signature(
- sd_bus_message *m,
- uint8_t h,
- const char *s,
- const char **ret) {
-
- size_t l;
- uint8_t *p;
-
- assert(m);
-
- /* dbus1 doesn't allow signatures over 32bit, let's enforce
- * this globally, to not risk convertability */
- l = strlen(s);
- if (l > 255)
- return -EINVAL;
-
- /* Signature "(yv)" where the variant contains "g" */
-
- if (BUS_MESSAGE_IS_GVARIANT(m))
- /* For gvariant the serialization is the same as for normal strings */
- return message_append_field_string(m, h, 'g', s, ret);
- else {
- /* (field id byte + (signature length + signature 'g' + NUL) + (string length + string + NUL)) */
- p = message_extend_fields(m, 8, 4 + 1 + l + 1, false);
- if (!p)
- return -ENOMEM;
-
- p[0] = h;
- p[1] = 1;
- p[2] = SD_BUS_TYPE_SIGNATURE;
- p[3] = 0;
- p[4] = l;
- memcpy(p + 5, s, l + 1);
-
- if (ret)
- *ret = (const char*) p + 5;
- }
-
- return 0;
-}
-
-static int message_append_field_uint32(sd_bus_message *m, uint8_t h, uint32_t x) {
- uint8_t *p;
-
- assert(m);
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- /* (field id byte + 7x padding + ((value + NUL + signature string 'u') */
-
- p = message_extend_fields(m, 8, 1 + 7 + 4 + 1 + 1, true);
- if (!p)
- return -ENOMEM;
-
- p[0] = h;
- memset(p+1, 0, 7);
- *((uint32_t*) (p + 8)) = x;
- p[12] = 0;
- p[13] = 'u';
- } else {
- /* (field id byte + (signature length + signature 'u' + NUL) + value) */
- p = message_extend_fields(m, 8, 4 + 4, false);
- if (!p)
- return -ENOMEM;
-
- p[0] = h;
- p[1] = 1;
- p[2] = SD_BUS_TYPE_UINT32;
- p[3] = 0;
-
- ((uint32_t*) p)[1] = x;
- }
-
- return 0;
-}
-
-int bus_message_from_header(
- sd_bus *bus,
- void *buffer,
- size_t length,
- int *fds,
- unsigned n_fds,
- const struct ucred *ucred,
- const char *label,
- size_t extra,
- sd_bus_message **ret) {
-
- sd_bus_message *m;
- struct bus_header *h;
- size_t a, label_sz;
-
- assert(buffer || length <= 0);
- assert(fds || n_fds <= 0);
- assert(ret);
-
- if (length < sizeof(struct bus_header))
- return -EBADMSG;
-
- h = buffer;
- if (h->version != 1 &&
- h->version != 2)
- return -EBADMSG;
-
- if (h->serial == 0)
- return -EBADMSG;
-
- if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID)
- return -EBADMSG;
-
- if (h->endian != BUS_LITTLE_ENDIAN &&
- h->endian != BUS_BIG_ENDIAN)
- return -EBADMSG;
-
- a = ALIGN(sizeof(sd_bus_message)) + ALIGN(extra);
-
- if (label) {
- label_sz = strlen(label);
- a += label_sz + 1;
- }
-
- m = malloc0(a);
- if (!m)
- return -ENOMEM;
-
- m->n_ref = 1;
- m->sealed = true;
- m->header = h;
- m->fds = fds;
- m->n_fds = n_fds;
-
- if (ucred) {
- m->creds.uid = ucred->uid;
- m->creds.pid = ucred->pid;
- m->creds.gid = ucred->gid;
- m->creds.mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_PID | SD_BUS_CREDS_GID;
- }
-
- if (label) {
- m->creds.label = (char*) m + ALIGN(sizeof(sd_bus_message)) + ALIGN(extra);
- memcpy(m->creds.label, label, label_sz + 1);
-
- m->creds.mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
- }
-
- if (bus)
- m->bus = sd_bus_ref(bus);
-
- *ret = m;
- return 0;
-}
-
-int bus_message_from_malloc(
- sd_bus *bus,
- void *buffer,
- size_t length,
- int *fds,
- unsigned n_fds,
- const struct ucred *ucred,
- const char *label,
- sd_bus_message **ret) {
-
- sd_bus_message *m;
- size_t sz;
- int r;
-
- r = bus_message_from_header(bus, buffer, length, fds, n_fds, ucred, label, 0, &m);
- if (r < 0)
- return r;
-
- if (length != BUS_MESSAGE_SIZE(m)) {
- r = -EBADMSG;
- goto fail;
- }
-
- sz = length - sizeof(struct bus_header) - ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m));
- if (sz > 0) {
- m->n_body_parts = 1;
- m->body.data = (uint8_t*) buffer + sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m));
- m->body.size = sz;
- m->body.sealed = true;
- m->body.memfd = -1;
- }
-
- m->n_iovec = 1;
- m->iovec = m->iovec_fixed;
- m->iovec[0].iov_base = buffer;
- m->iovec[0].iov_len = length;
-
- r = bus_message_parse_fields(m);
- if (r < 0)
- goto fail;
-
- /* We take possession of the memory and fds now */
- m->free_header = true;
- m->free_fds = true;
-
- *ret = m;
- return 0;
-
-fail:
- message_free(m);
- return r;
-}
-
-static sd_bus_message *message_new(sd_bus *bus, uint8_t type) {
- sd_bus_message *m;
-
- m = malloc0(ALIGN(sizeof(sd_bus_message)) + sizeof(struct bus_header));
- if (!m)
- return NULL;
-
- m->n_ref = 1;
- m->header = (struct bus_header*) ((uint8_t*) m + ALIGN(sizeof(struct sd_bus_message)));
- m->header->endian = BUS_NATIVE_ENDIAN;
- m->header->type = type;
- m->header->version = bus ? bus->message_version : 1;
- m->allow_fds = !bus || bus->can_fds || (bus->state != BUS_HELLO && bus->state != BUS_RUNNING);
- m->root_container.need_offsets = BUS_MESSAGE_IS_GVARIANT(m);
-
- if (bus)
- m->bus = sd_bus_ref(bus);
-
- return m;
-}
-
-_public_ int sd_bus_message_new_signal(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *member,
- sd_bus_message **m) {
-
- sd_bus_message *t;
- int r;
-
- assert_return(!bus || bus->state != BUS_UNSET, -ENOTCONN);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(interface_name_is_valid(interface), -EINVAL);
- assert_return(member_name_is_valid(member), -EINVAL);
- assert_return(m, -EINVAL);
-
- t = message_new(bus, SD_BUS_MESSAGE_SIGNAL);
- if (!t)
- return -ENOMEM;
-
- t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
-
- r = message_append_field_string(t, BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path);
- if (r < 0)
- goto fail;
- r = message_append_field_string(t, BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface);
- if (r < 0)
- goto fail;
- r = message_append_field_string(t, BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member);
- if (r < 0)
- goto fail;
-
- *m = t;
- return 0;
-
-fail:
- sd_bus_message_unref(t);
- return r;
-}
-
-_public_ int sd_bus_message_new_method_call(
- sd_bus *bus,
- const char *destination,
- const char *path,
- const char *interface,
- const char *member,
- sd_bus_message **m) {
-
- sd_bus_message *t;
- int r;
-
- assert_return(!bus || bus->state != BUS_UNSET, -ENOTCONN);
- assert_return(!destination || service_name_is_valid(destination), -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(!interface || interface_name_is_valid(interface), -EINVAL);
- assert_return(member_name_is_valid(member), -EINVAL);
- assert_return(m, -EINVAL);
-
- t = message_new(bus, SD_BUS_MESSAGE_METHOD_CALL);
- if (!t)
- return -ENOMEM;
-
- r = message_append_field_string(t, BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path);
- if (r < 0)
- goto fail;
- r = message_append_field_string(t, BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member);
- if (r < 0)
- goto fail;
-
- if (interface) {
- r = message_append_field_string(t, BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface);
- if (r < 0)
- goto fail;
- }
-
- if (destination) {
- r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &t->destination);
- if (r < 0)
- goto fail;
- }
-
- *m = t;
- return 0;
-
-fail:
- message_free(t);
- return r;
-}
-
-static int message_new_reply(
- sd_bus_message *call,
- uint8_t type,
- sd_bus_message **m) {
-
- sd_bus_message *t;
- int r;
-
- assert_return(call, -EINVAL);
- assert_return(call->sealed, -EPERM);
- assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
- assert_return(!call->bus || call->bus->state != BUS_UNSET, -ENOTCONN);
- assert_return(m, -EINVAL);
-
- t = message_new(call->bus, type);
- if (!t)
- return -ENOMEM;
-
- t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
- t->reply_cookie = BUS_MESSAGE_COOKIE(call);
-
- r = message_append_field_uint32(t, BUS_MESSAGE_HEADER_REPLY_SERIAL, t->reply_cookie);
- if (r < 0)
- goto fail;
-
- if (call->sender) {
- r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, call->sender, &t->destination);
- if (r < 0)
- goto fail;
- }
-
- t->dont_send = !!(call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED);
- t->enforced_reply_signature = call->enforced_reply_signature;
-
- *m = t;
- return 0;
-
-fail:
- message_free(t);
- return r;
-}
-
-_public_ int sd_bus_message_new_method_return(
- sd_bus_message *call,
- sd_bus_message **m) {
-
- return message_new_reply(call, SD_BUS_MESSAGE_METHOD_RETURN, m);
-}
-
-_public_ int sd_bus_message_new_method_error(
- sd_bus_message *call,
- const sd_bus_error *e,
- sd_bus_message **m) {
-
- sd_bus_message *t;
- int r;
-
- assert_return(sd_bus_error_is_set(e), -EINVAL);
- assert_return(m, -EINVAL);
-
- r = message_new_reply(call, SD_BUS_MESSAGE_METHOD_ERROR, &t);
- if (r < 0)
- return r;
-
- r = message_append_field_string(t, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, e->name, &t->error.name);
- if (r < 0)
- goto fail;
-
- if (e->message) {
- r = message_append_basic(t, SD_BUS_TYPE_STRING, e->message, (const void**) &t->error.message);
- if (r < 0)
- goto fail;
- }
-
- t->error._need_free = -1;
-
- *m = t;
- return 0;
-
-fail:
- message_free(t);
- return r;
-}
-
-_public_ int sd_bus_message_new_method_errorf(
- sd_bus_message *call,
- sd_bus_message **m,
- const char *name,
- const char *format,
- ...) {
-
- _cleanup_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- va_list ap;
-
- assert_return(name, -EINVAL);
- assert_return(m, -EINVAL);
-
- va_start(ap, format);
- bus_error_setfv(&error, name, format, ap);
- va_end(ap);
-
- return sd_bus_message_new_method_error(call, &error, m);
-}
-
-_public_ int sd_bus_message_new_method_errno(
- sd_bus_message *call,
- int error,
- const sd_bus_error *p,
- sd_bus_message **m) {
-
- _cleanup_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
-
- if (sd_bus_error_is_set(p))
- return sd_bus_message_new_method_error(call, p, m);
-
- sd_bus_error_set_errno(&berror, error);
-
- return sd_bus_message_new_method_error(call, &berror, m);
-}
-
-_public_ int sd_bus_message_new_method_errnof(
- sd_bus_message *call,
- sd_bus_message **m,
- int error,
- const char *format,
- ...) {
-
- _cleanup_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
- va_list ap;
-
- va_start(ap, format);
- bus_error_set_errnofv(&berror, error, format, ap);
- va_end(ap);
-
- return sd_bus_message_new_method_error(call, &berror, m);
-}
-
-int bus_message_new_synthetic_error(
- sd_bus *bus,
- uint64_t cookie,
- const sd_bus_error *e,
- sd_bus_message **m) {
-
- sd_bus_message *t;
- int r;
-
- assert(sd_bus_error_is_set(e));
- assert(m);
-
- t = message_new(bus, SD_BUS_MESSAGE_METHOD_ERROR);
- if (!t)
- return -ENOMEM;
-
- t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
- t->reply_cookie = cookie;
-
- r = message_append_field_uint32(t, BUS_MESSAGE_HEADER_REPLY_SERIAL, t->reply_cookie);
- if (r < 0)
- goto fail;
-
- if (bus && bus->unique_name) {
- r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, bus->unique_name, &t->destination);
- if (r < 0)
- goto fail;
- }
-
- r = message_append_field_string(t, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, e->name, &t->error.name);
- if (r < 0)
- goto fail;
-
- if (e->message) {
- r = message_append_basic(t, SD_BUS_TYPE_STRING, e->message, (const void**) &t->error.message);
- if (r < 0)
- goto fail;
- }
-
- t->error._need_free = -1;
-
- *m = t;
- return 0;
-
-fail:
- message_free(t);
- return r;
-}
-
-_public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) {
- assert_return(m, NULL);
-
- assert(m->n_ref > 0);
- m->n_ref++;
-
- return m;
-}
-
-_public_ sd_bus_message* sd_bus_message_unref(sd_bus_message *m) {
-
- if (!m)
- return NULL;
-
- assert(m->n_ref > 0);
- m->n_ref--;
-
- if (m->n_ref <= 0)
- message_free(m);
-
- return NULL;
-}
-
-_public_ int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type) {
- assert_return(m, -EINVAL);
- assert_return(type, -EINVAL);
-
- *type = m->header->type;
- return 0;
-}
-
-_public_ int sd_bus_message_get_cookie(sd_bus_message *m, uint64_t *cookie) {
- assert_return(m, -EINVAL);
- assert_return(cookie, -EINVAL);
- assert_return(m->header->serial != 0, -ENODATA);
-
- *cookie = BUS_MESSAGE_COOKIE(m);
- return 0;
-}
-
-_public_ int sd_bus_message_get_reply_cookie(sd_bus_message *m, uint64_t *cookie) {
- assert_return(m, -EINVAL);
- assert_return(cookie, -EINVAL);
- assert_return(m->reply_cookie != 0, -ENODATA);
-
- *cookie = m->reply_cookie;
- return 0;
-}
-
-_public_ int sd_bus_message_get_no_reply(sd_bus_message *m) {
- assert_return(m, -EINVAL);
-
- return m->header->type == SD_BUS_MESSAGE_METHOD_CALL ? !!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) : 0;
-}
-
-_public_ int sd_bus_message_get_no_auto_start(sd_bus_message *m) {
- assert_return(m, -EINVAL);
-
- return !!(m->header->flags & BUS_MESSAGE_NO_AUTO_START);
-}
-
-_public_ const char *sd_bus_message_get_path(sd_bus_message *m) {
- assert_return(m, NULL);
-
- return m->path;
-}
-
-_public_ const char *sd_bus_message_get_interface(sd_bus_message *m) {
- assert_return(m, NULL);
-
- return m->interface;
-}
-
-_public_ const char *sd_bus_message_get_member(sd_bus_message *m) {
- assert_return(m, NULL);
-
- return m->member;
-}
-
-_public_ const char *sd_bus_message_get_destination(sd_bus_message *m) {
- assert_return(m, NULL);
-
- return m->destination;
-}
-
-_public_ const char *sd_bus_message_get_sender(sd_bus_message *m) {
- assert_return(m, NULL);
-
- return m->sender;
-}
-
-_public_ const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m) {
- assert_return(m, NULL);
- assert_return(sd_bus_error_is_set(&m->error), NULL);
-
- return &m->error;
-}
-
-_public_ int sd_bus_message_get_monotonic_timestamp(sd_bus_message *m, uint64_t *usec) {
- assert_return(m, -EINVAL);
- assert_return(usec, -EINVAL);
- assert_return(m->monotonic > 0, -ENODATA);
-
- *usec = m->monotonic;
- return 0;
-}
-
-_public_ int sd_bus_message_get_realtime_timestamp(sd_bus_message *m, uint64_t *usec) {
- assert_return(m, -EINVAL);
- assert_return(usec, -EINVAL);
- assert_return(m->realtime > 0, -ENODATA);
-
- *usec = m->realtime;
- return 0;
-}
-
-_public_ sd_bus_creds *sd_bus_message_get_creds(sd_bus_message *m) {
- assert_return(m, NULL);
-
- if (m->creds.mask == 0)
- return NULL;
-
- return &m->creds;
-}
-
-_public_ int sd_bus_message_is_signal(sd_bus_message *m,
- const char *interface,
- const char *member) {
- assert_return(m, -EINVAL);
-
- if (m->header->type != SD_BUS_MESSAGE_SIGNAL)
- return 0;
-
- if (interface && (!m->interface || !streq(m->interface, interface)))
- return 0;
-
- if (member && (!m->member || !streq(m->member, member)))
- return 0;
-
- return 1;
-}
-
-_public_ int sd_bus_message_is_method_call(sd_bus_message *m,
- const char *interface,
- const char *member) {
- assert_return(m, -EINVAL);
-
- if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
- return 0;
-
- if (interface && (!m->interface || !streq(m->interface, interface)))
- return 0;
-
- if (member && (!m->member || !streq(m->member, member)))
- return 0;
-
- return 1;
-}
-
-_public_ int sd_bus_message_is_method_error(sd_bus_message *m, const char *name) {
- assert_return(m, -EINVAL);
-
- if (m->header->type != SD_BUS_MESSAGE_METHOD_ERROR)
- return 0;
-
- if (name && (!m->error.name || !streq(m->error.name, name)))
- return 0;
-
- return 1;
-}
-
-_public_ int sd_bus_message_set_no_reply(sd_bus_message *m, int b) {
- assert_return(m, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EPERM);
-
- if (b)
- m->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
- else
- m->header->flags &= ~BUS_MESSAGE_NO_REPLY_EXPECTED;
-
- return 0;
-}
-
-_public_ int sd_bus_message_set_no_auto_start(sd_bus_message *m, int b) {
- assert_return(m, -EINVAL);
- assert_return(!m->sealed, -EPERM);
-
- if (b)
- m->header->flags |= BUS_MESSAGE_NO_AUTO_START;
- else
- m->header->flags &= ~BUS_MESSAGE_NO_AUTO_START;
-
- return 0;
-}
-
-static struct bus_container *message_get_container(sd_bus_message *m) {
- assert(m);
-
- if (m->n_containers == 0)
- return &m->root_container;
-
- assert(m->containers);
- return m->containers + m->n_containers - 1;
-}
-
-struct bus_body_part *message_append_part(sd_bus_message *m) {
- struct bus_body_part *part;
-
- assert(m);
-
- if (m->poisoned)
- return NULL;
-
- if (m->n_body_parts <= 0) {
- part = &m->body;
- zero(*part);
- } else {
- assert(m->body_end);
-
- part = new0(struct bus_body_part, 1);
- if (!part) {
- m->poisoned = true;
- return NULL;
- }
-
- m->body_end->next = part;
- }
-
- part->memfd = -1;
- m->body_end = part;
- m->n_body_parts ++;
-
- return part;
-}
-
-static void part_zero(struct bus_body_part *part, size_t sz) {
- assert(part);
- assert(sz > 0);
- assert(sz < 8);
-
- /* All other fields can be left in their defaults */
- assert(!part->data);
- assert(part->memfd < 0);
-
- part->size = sz;
- part->is_zero = true;
- part->sealed = true;
-}
-
-static int part_make_space(
- struct sd_bus_message *m,
- struct bus_body_part *part,
- size_t sz,
- void **q) {
-
- void *n;
- int r;
-
- assert(m);
- assert(part);
- assert(!part->sealed);
-
- if (m->poisoned)
- return -ENOMEM;
-
- if (!part->data && part->memfd < 0)
- part->memfd = bus_kernel_pop_memfd(m->bus, &part->data, &part->mapped, &part->allocated);
-
- if (part->memfd >= 0) {
-
- if (part->allocated == 0 || sz > part->allocated) {
- uint64_t new_allocated;
-
- new_allocated = PAGE_ALIGN(sz > 0 ? 2 * sz : 1);
- r = ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &new_allocated);
- if (r < 0) {
- m->poisoned = true;
- return -errno;
- }
-
- part->allocated = new_allocated;
- }
-
- if (!part->data || sz > part->mapped) {
- size_t psz;
-
- psz = PAGE_ALIGN(sz > 0 ? sz : 1);
- if (part->mapped <= 0)
- n = mmap(NULL, psz, PROT_READ|PROT_WRITE, MAP_SHARED, part->memfd, 0);
- else
- n = mremap(part->data, part->mapped, psz, MREMAP_MAYMOVE);
-
- if (n == MAP_FAILED) {
- m->poisoned = true;
- return -errno;
- }
-
- part->mapped = psz;
- part->data = n;
- }
-
- part->munmap_this = true;
- } else {
- if (part->allocated == 0 || sz > part->allocated) {
- size_t new_allocated;
-
- new_allocated = sz > 0 ? 2 * sz : 64;
- n = realloc(part->data, new_allocated);
- if (!n) {
- m->poisoned = true;
- return -ENOMEM;
- }
-
- part->data = n;
- part->allocated = new_allocated;
- part->free_this = true;
- }
- }
-
- if (q)
- *q = part->data ? (uint8_t*) part->data + part->size : NULL;
-
- part->size = sz;
- return 0;
-}
-
-static int message_add_offset(sd_bus_message *m, size_t offset) {
- struct bus_container *c;
-
- assert(m);
- assert(BUS_MESSAGE_IS_GVARIANT(m));
-
- /* Add offset to current container, unless this is the first
- * item in it, which will have the 0 offset, which we can
- * ignore. */
- c = message_get_container(m);
-
- if (!c->need_offsets)
- return 0;
-
- if (!GREEDY_REALLOC(c->offsets, c->offsets_allocated, c->n_offsets + 1))
- return -ENOMEM;
-
- c->offsets[c->n_offsets++] = offset;
- return 0;
-}
-
-static void message_extend_containers(sd_bus_message *m, size_t expand) {
- struct bus_container *c;
-
- assert(m);
-
- if (expand <= 0)
- return;
-
- /* Update counters */
- for (c = m->containers; c < m->containers + m->n_containers; c++) {
-
- if (c->array_size)
- *c->array_size += expand;
- }
-}
-
-static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz, bool add_offset) {
- size_t start_body, end_body, padding, added;
- void *p;
- int r;
-
- assert(m);
- assert(align > 0);
- assert(!m->sealed);
-
- if (m->poisoned)
- return NULL;
-
- start_body = ALIGN_TO((size_t) m->header->body_size, align);
- end_body = start_body + sz;
-
- padding = start_body - m->header->body_size;
- added = padding + sz;
-
- /* Check for 32bit overflows */
- if (end_body > (size_t) ((uint32_t) -1)) {
- m->poisoned = true;
- return NULL;
- }
-
- if (added > 0) {
- struct bus_body_part *part = NULL;
- bool add_new_part;
-
- add_new_part =
- m->n_body_parts <= 0 ||
- m->body_end->sealed ||
- padding != ALIGN_TO(m->body_end->size, align) - m->body_end->size;
-
- if (add_new_part) {
- if (padding > 0) {
- part = message_append_part(m);
- if (!part)
- return NULL;
-
- part_zero(part, padding);
- }
-
- part = message_append_part(m);
- if (!part)
- return NULL;
-
- r = part_make_space(m, part, sz, &p);
- if (r < 0)
- return NULL;
- } else {
- struct bus_container *c;
- void *op;
- size_t os, start_part, end_part;
-
- part = m->body_end;
- op = part->data;
- os = part->size;
-
- start_part = ALIGN_TO(part->size, align);
- end_part = start_part + sz;
-
- r = part_make_space(m, part, end_part, &p);
- if (r < 0)
- return NULL;
-
- if (padding > 0) {
- memset(p, 0, padding);
- p = (uint8_t*) p + padding;
- }
-
- /* Readjust pointers */
- for (c = m->containers; c < m->containers + m->n_containers; c++)
- c->array_size = adjust_pointer(c->array_size, op, os, part->data);
-
- m->error.message = (const char*) adjust_pointer(m->error.message, op, os, part->data);
- }
- } else
- /* Return something that is not NULL and is aligned */
- p = (uint8_t *) NULL + align;
-
- m->header->body_size = end_body;
- message_extend_containers(m, added);
-
- if (add_offset) {
- r = message_add_offset(m, end_body);
- if (r < 0) {
- m->poisoned = true;
- return NULL;
- }
- }
-
- return p;
-}
-
-static int message_push_fd(sd_bus_message *m, int fd) {
- int *f, copy;
-
- assert(m);
-
- if (fd < 0)
- return -EINVAL;
-
- if (!m->allow_fds)
- return -ENOTSUP;
-
- copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (copy < 0)
- return -errno;
-
- f = realloc(m->fds, sizeof(int) * (m->n_fds + 1));
- if (!f) {
- m->poisoned = true;
- close_nointr_nofail(copy);
- return -ENOMEM;
- }
-
- m->fds = f;
- m->fds[m->n_fds] = copy;
- m->free_fds = true;
-
- return copy;
-}
-
-int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored) {
- _cleanup_close_ int fd = -1;
- struct bus_container *c;
- ssize_t align, sz;
- void *a;
-
- assert_return(m, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(bus_type_is_basic(type), -EINVAL);
- assert_return(!m->poisoned, -ESTALE);
-
- c = message_get_container(m);
-
- if (c->signature && c->signature[c->index]) {
- /* Container signature is already set */
-
- if (c->signature[c->index] != type)
- return -ENXIO;
- } else {
- char *e;
-
- /* Maybe we can append to the signature? But only if this is the top-level container*/
- if (c->enclosing != 0)
- return -ENXIO;
-
- e = strextend(&c->signature, CHAR_TO_STR(type), NULL);
- if (!e) {
- m->poisoned = true;
- return -ENOMEM;
- }
- }
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- uint8_t u8;
- uint32_t u32;
-
- switch (type) {
-
- case SD_BUS_TYPE_SIGNATURE:
- case SD_BUS_TYPE_STRING:
- p = strempty(p);
-
- /* Fall through... */
- case SD_BUS_TYPE_OBJECT_PATH:
- if (!p)
- return -EINVAL;
-
- align = 1;
- sz = strlen(p) + 1;
- break;
-
- case SD_BUS_TYPE_BOOLEAN:
-
- u8 = p && *(int*) p;
- p = &u8;
-
- align = sz = 1;
- break;
-
- case SD_BUS_TYPE_UNIX_FD:
-
- if (!p)
- return -EINVAL;
-
- fd = message_push_fd(m, *(int*) p);
- if (fd < 0)
- return fd;
-
- u32 = m->n_fds;
- p = &u32;
-
- align = sz = 4;
- break;
-
- default:
- align = bus_gvariant_get_alignment(CHAR_TO_STR(type));
- sz = bus_gvariant_get_size(CHAR_TO_STR(type));
- break;
- }
-
- assert(align > 0);
- assert(sz > 0);
-
- a = message_extend_body(m, align, sz, true);
- if (!a)
- return -ENOMEM;
-
- memcpy(a, p, sz);
-
- if (stored)
- *stored = (const uint8_t*) a;
-
- } else {
- uint32_t u32;
-
- switch (type) {
-
- case SD_BUS_TYPE_STRING:
- /* To make things easy we'll serialize a NULL string
- * into the empty string */
- p = strempty(p);
-
- /* Fall through... */
- case SD_BUS_TYPE_OBJECT_PATH:
-
- if (!p)
- return -EINVAL;
-
- align = 4;
- sz = 4 + strlen(p) + 1;
- break;
-
- case SD_BUS_TYPE_SIGNATURE:
-
- p = strempty(p);
-
- align = 1;
- sz = 1 + strlen(p) + 1;
- break;
-
- case SD_BUS_TYPE_BOOLEAN:
-
- u32 = p && *(int*) p;
- p = &u32;
-
- align = sz = 4;
- break;
-
- case SD_BUS_TYPE_UNIX_FD:
-
- if (!p)
- return -EINVAL;
-
- fd = message_push_fd(m, *(int*) p);
- if (fd < 0)
- return fd;
-
- u32 = m->n_fds;
- p = &u32;
-
- align = sz = 4;
- break;
-
- default:
- align = bus_type_get_alignment(type);
- sz = bus_type_get_size(type);
- break;
- }
-
- assert(align > 0);
- assert(sz > 0);
-
- a = message_extend_body(m, align, sz, false);
- if (!a)
- return -ENOMEM;
-
- if (type == SD_BUS_TYPE_STRING || type == SD_BUS_TYPE_OBJECT_PATH) {
- *(uint32_t*) a = sz - 5;
- memcpy((uint8_t*) a + 4, p, sz - 4);
-
- if (stored)
- *stored = (const uint8_t*) a + 4;
-
- } else if (type == SD_BUS_TYPE_SIGNATURE) {
- *(uint8_t*) a = sz - 2;
- memcpy((uint8_t*) a + 1, p, sz - 1);
-
- if (stored)
- *stored = (const uint8_t*) a + 1;
- } else {
- memcpy(a, p, sz);
-
- if (stored)
- *stored = a;
- }
- }
-
- if (type == SD_BUS_TYPE_UNIX_FD)
- m->n_fds ++;
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- c->index++;
-
- fd = -1;
- return 0;
-}
-
-_public_ int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p) {
- return message_append_basic(m, type, p, NULL);
-}
-
-_public_ int sd_bus_message_append_string_space(
- sd_bus_message *m,
- size_t size,
- char **s) {
-
- struct bus_container *c;
- void *a;
-
- assert_return(m, -EINVAL);
- assert_return(s, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(!m->poisoned, -ESTALE);
-
- c = message_get_container(m);
-
- if (c->signature && c->signature[c->index]) {
- /* Container signature is already set */
-
- if (c->signature[c->index] != SD_BUS_TYPE_STRING)
- return -ENXIO;
- } else {
- char *e;
-
- /* Maybe we can append to the signature? But only if this is the top-level container*/
- if (c->enclosing != 0)
- return -ENXIO;
-
- e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRING), NULL);
- if (!e) {
- m->poisoned = true;
- return -ENOMEM;
- }
- }
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- a = message_extend_body(m, 1, size + 1, true);
- if (!a)
- return -ENOMEM;
-
- *s = a;
- } else {
- a = message_extend_body(m, 4, 4 + size + 1, false);
- if (!a)
- return -ENOMEM;
-
- *(uint32_t*) a = size;
- *s = (char*) a + 4;
- }
-
- (*s)[size] = 0;
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- c->index++;
-
- return 0;
-}
-
-_public_ int sd_bus_message_append_string_iovec(
- sd_bus_message *m,
- const struct iovec *iov,
- unsigned n) {
-
- size_t size;
- unsigned i;
- char *p;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(iov || n == 0, -EINVAL);
- assert_return(!m->poisoned, -ESTALE);
-
- size = IOVEC_TOTAL_SIZE(iov, n);
-
- r = sd_bus_message_append_string_space(m, size, &p);
- if (r < 0)
- return r;
-
- for (i = 0; i < n; i++) {
-
- if (iov[i].iov_base)
- memcpy(p, iov[i].iov_base, iov[i].iov_len);
- else
- memset(p, ' ', iov[i].iov_len);
-
- p += iov[i].iov_len;
- }
-
- return 0;
-}
-
-static int bus_message_open_array(
- sd_bus_message *m,
- struct bus_container *c,
- const char *contents,
- uint32_t **array_size,
- size_t *begin,
- bool *need_offsets) {
-
- unsigned nindex;
- int alignment, r;
-
- assert(m);
- assert(c);
- assert(contents);
- assert(array_size);
- assert(begin);
- assert(need_offsets);
-
- if (!signature_is_single(contents, true))
- return -EINVAL;
-
- if (c->signature && c->signature[c->index]) {
-
- /* Verify the existing signature */
-
- if (c->signature[c->index] != SD_BUS_TYPE_ARRAY)
- return -ENXIO;
-
- if (!startswith(c->signature + c->index + 1, contents))
- return -ENXIO;
-
- nindex = c->index + 1 + strlen(contents);
- } else {
- char *e;
-
- if (c->enclosing != 0)
- return -ENXIO;
-
- /* Extend the existing signature */
-
- e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_ARRAY), contents, NULL);
- if (!e) {
- m->poisoned = true;
- return -ENOMEM;
- }
-
- nindex = e - c->signature;
- }
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- alignment = bus_gvariant_get_alignment(contents);
- if (alignment < 0)
- return alignment;
-
- /* Add alignment padding and add to offset list */
- if (!message_extend_body(m, alignment, 0, false))
- return -ENOMEM;
-
- r = bus_gvariant_is_fixed_size(contents);
- if (r < 0)
- return r;
-
- *begin = m->header->body_size;
- *need_offsets = r == 0;
- } else {
- void *a, *op;
- size_t os;
- struct bus_body_part *o;
-
- alignment = bus_type_get_alignment(contents[0]);
- if (alignment < 0)
- return alignment;
-
- a = message_extend_body(m, 4, 4, false);
- if (!a)
- return -ENOMEM;
-
- o = m->body_end;
- op = m->body_end->data;
- os = m->body_end->size;
-
- /* Add alignment between size and first element */
- if (!message_extend_body(m, alignment, 0, false))
- return -ENOMEM;
-
- /* location of array size might have changed so let's readjust a */
- if (o == m->body_end)
- a = adjust_pointer(a, op, os, m->body_end->data);
-
- *(uint32_t*) a = 0;
- *array_size = a;
- }
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- c->index = nindex;
-
- return 0;
-}
-
-static int bus_message_open_variant(
- sd_bus_message *m,
- struct bus_container *c,
- const char *contents) {
-
- assert(m);
- assert(c);
- assert(contents);
-
- if (!signature_is_single(contents, false))
- return -EINVAL;
-
- if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN)
- return -EINVAL;
-
- if (c->signature && c->signature[c->index]) {
-
- if (c->signature[c->index] != SD_BUS_TYPE_VARIANT)
- return -ENXIO;
-
- } else {
- char *e;
-
- if (c->enclosing != 0)
- return -ENXIO;
-
- e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_VARIANT), NULL);
- if (!e) {
- m->poisoned = true;
- return -ENOMEM;
- }
- }
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- /* Variants are always aligned to 8 */
-
- if (!message_extend_body(m, 8, 0, false))
- return -ENOMEM;
-
- } else {
- size_t l;
- void *a;
-
- l = strlen(contents);
- a = message_extend_body(m, 1, 1 + l + 1, false);
- if (!a)
- return -ENOMEM;
-
- *(uint8_t*) a = l;
- memcpy((uint8_t*) a + 1, contents, l + 1);
- }
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- c->index++;
-
- return 0;
-}
-
-static int bus_message_open_struct(
- sd_bus_message *m,
- struct bus_container *c,
- const char *contents,
- size_t *begin,
- bool *need_offsets) {
-
- size_t nindex;
- int r;
-
- assert(m);
- assert(c);
- assert(contents);
- assert(begin);
- assert(need_offsets);
-
- if (!signature_is_valid(contents, false))
- return -EINVAL;
-
- if (c->signature && c->signature[c->index]) {
- size_t l;
-
- l = strlen(contents);
-
- if (c->signature[c->index] != SD_BUS_TYPE_STRUCT_BEGIN ||
- !startswith(c->signature + c->index + 1, contents) ||
- c->signature[c->index + 1 + l] != SD_BUS_TYPE_STRUCT_END)
- return -ENXIO;
-
- nindex = c->index + 1 + l + 1;
- } else {
- char *e;
-
- if (c->enclosing != 0)
- return -ENXIO;
-
- e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_BEGIN), contents, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_END), NULL);
- if (!e) {
- m->poisoned = true;
- return -ENOMEM;
- }
-
- nindex = e - c->signature;
- }
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- int alignment;
-
- alignment = bus_gvariant_get_alignment(contents);
- if (alignment < 0)
- return alignment;
-
- if (!message_extend_body(m, alignment, 0, false))
- return -ENOMEM;
-
- r = bus_gvariant_is_fixed_size(contents);
- if (r < 0)
- return r;
-
- *begin = m->header->body_size;
- *need_offsets = r == 0;
- } else {
- /* Align contents to 8 byte boundary */
- if (!message_extend_body(m, 8, 0, false))
- return -ENOMEM;
- }
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- c->index = nindex;
-
- return 0;
-}
-
-static int bus_message_open_dict_entry(
- sd_bus_message *m,
- struct bus_container *c,
- const char *contents,
- size_t *begin,
- bool *need_offsets) {
-
- int r;
-
- assert(m);
- assert(c);
- assert(contents);
- assert(begin);
- assert(need_offsets);
-
- if (!signature_is_pair(contents))
- return -EINVAL;
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- return -ENXIO;
-
- if (c->signature && c->signature[c->index]) {
- size_t l;
-
- l = strlen(contents);
-
- if (c->signature[c->index] != SD_BUS_TYPE_DICT_ENTRY_BEGIN ||
- !startswith(c->signature + c->index + 1, contents) ||
- c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END)
- return -ENXIO;
- } else
- return -ENXIO;
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- int alignment;
-
- alignment = bus_gvariant_get_alignment(contents);
- if (alignment < 0)
- return alignment;
-
- if (!message_extend_body(m, alignment, 0, false))
- return -ENOMEM;
-
- r = bus_gvariant_is_fixed_size(contents);
- if (r < 0)
- return r;
-
- *begin = m->header->body_size;
- *need_offsets = r == 0;
- } else {
- /* Align contents to 8 byte boundary */
- if (!message_extend_body(m, 8, 0, false))
- return -ENOMEM;
- }
-
- return 0;
-}
-
-_public_ int sd_bus_message_open_container(
- sd_bus_message *m,
- char type,
- const char *contents) {
-
- struct bus_container *c, *w;
- uint32_t *array_size = NULL;
- char *signature;
- size_t before, begin;
- bool need_offsets = false;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(contents, -EINVAL);
- assert_return(!m->poisoned, -ESTALE);
-
- /* Make sure we have space for one more container */
- if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1)) {
- m->poisoned = true;
- return -ENOMEM;
- }
-
- c = message_get_container(m);
-
- signature = strdup(contents);
- if (!signature) {
- m->poisoned = true;
- return -ENOMEM;
- }
-
- /* Save old index in the parent container, in case we have to
- * abort this container */
- c->saved_index = c->index;
- before = m->header->body_size;
-
- if (type == SD_BUS_TYPE_ARRAY)
- r = bus_message_open_array(m, c, contents, &array_size, &begin, &need_offsets);
- else if (type == SD_BUS_TYPE_VARIANT)
- r = bus_message_open_variant(m, c, contents);
- else if (type == SD_BUS_TYPE_STRUCT)
- r = bus_message_open_struct(m, c, contents, &begin, &need_offsets);
- else if (type == SD_BUS_TYPE_DICT_ENTRY)
- r = bus_message_open_dict_entry(m, c, contents, &begin, &need_offsets);
- else
- r = -EINVAL;
-
- if (r < 0) {
- free(signature);
- return r;
- }
-
- /* OK, let's fill it in */
- w = m->containers + m->n_containers++;
- w->enclosing = type;
- w->signature = signature;
- w->index = 0;
- w->array_size = array_size;
- w->before = before;
- w->begin = begin;
- w->n_offsets = w->offsets_allocated = 0;
- w->offsets = NULL;
- w->need_offsets = need_offsets;
-
- return 0;
-}
-
-static size_t determine_word_size(size_t sz, size_t extra) {
- if (sz + extra <= 0xFF)
- return 1;
- else if (sz + extra*2 <= 0xFFFF)
- return 2;
- else if (sz + extra*4 <= 0xFFFFFFFF)
- return 4;
- else
- return 8;
-}
-
-static size_t read_word_le(void *p, size_t sz) {
- union {
- uint16_t u16;
- uint32_t u32;
- uint64_t u64;
- } x;
-
- assert(p);
-
- if (sz == 1)
- return *(uint8_t*) p;
-
- memcpy(&x, p, sz);
-
- if (sz == 2)
- return le16toh(x.u16);
- else if (sz == 4)
- return le32toh(x.u32);
- else if (sz == 8)
- return le64toh(x.u64);
-
- assert_not_reached("unknown word width");
-}
-
-static void write_word_le(void *p, size_t sz, size_t value) {
- union {
- uint16_t u16;
- uint32_t u32;
- uint64_t u64;
- } x;
-
- assert(p);
- assert(sz == 8 || (value < (1ULL << (sz*8))));
-
- if (sz == 1) {
- *(uint8_t*) p = value;
- return;
- } else if (sz == 2)
- x.u16 = htole16((uint16_t) value);
- else if (sz == 4)
- x.u32 = htole32((uint32_t) value);
- else if (sz == 8)
- x.u64 = htole64((uint64_t) value);
- else
- assert_not_reached("unknown word width");
-
- memcpy(p, &x, sz);
-}
-
-static int bus_message_close_array(sd_bus_message *m, struct bus_container *c) {
-
- assert(m);
- assert(c);
-
- if (!BUS_MESSAGE_IS_GVARIANT(m))
- return 0;
-
- if (c->need_offsets) {
- size_t payload, sz, i;
- uint8_t *a;
-
- /* Variable-width arrays */
-
- payload = c->n_offsets > 0 ? c->offsets[c->n_offsets-1] - c->begin : 0;
- sz = determine_word_size(payload, c->n_offsets);
-
- a = message_extend_body(m, 1, sz * c->n_offsets, true);
- if (!a)
- return -ENOMEM;
-
- for (i = 0; i < c->n_offsets; i++)
- write_word_le(a + sz*i, sz, c->offsets[i] - c->begin);
- } else {
- void *a;
-
- /* Fixed-width or empty arrays */
-
- a = message_extend_body(m, 1, 0, true); /* let's add offset to parent */
- if (!a)
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static int bus_message_close_variant(sd_bus_message *m, struct bus_container *c) {
- uint8_t *a;
- size_t l;
-
- assert(m);
- assert(c);
-
- if (!BUS_MESSAGE_IS_GVARIANT(m))
- return 0;
-
- l = strlen(c->signature);
-
- a = message_extend_body(m, 1, 1 + l, true);
- if (!a)
- return -ENOMEM;
-
- a[0] = 0;
- memcpy(a+1, c->signature, l);
-
- return 0;
-}
-
-static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c, bool add_offset) {
- size_t n_variable = 0;
- unsigned i = 0;
- const char *p;
- uint8_t *a;
- int r;
-
- assert(m);
- assert(c);
-
- if (!BUS_MESSAGE_IS_GVARIANT(m))
- return 0;
-
- p = strempty(c->signature);
- while (*p != 0) {
- size_t n;
-
- r = signature_element_length(p, &n);
- if (r < 0)
- return r;
- else {
- char t[n+1];
-
- memcpy(t, p, n);
- t[n] = 0;
-
- r = bus_gvariant_is_fixed_size(t);
- if (r < 0)
- return r;
- }
-
- assert(!c->need_offsets || i <= c->n_offsets);
-
- /* We need to add an offset for each item that has a
- * variable size and that is not the last one in the
- * list */
- if (r == 0 && p[n] != 0)
- n_variable++;
-
- i++;
- p += n;
- }
-
- assert(!c->need_offsets || i == c->n_offsets);
- assert(c->need_offsets || n_variable == 0);
-
- if (n_variable <= 0) {
- a = message_extend_body(m, 1, 0, add_offset);
- if (!a)
- return -ENOMEM;
- } else {
- size_t sz;
- unsigned j;
-
- assert(c->offsets[c->n_offsets-1] == m->header->body_size);
-
- sz = determine_word_size(m->header->body_size - c->begin, n_variable);
-
- a = message_extend_body(m, 1, sz * n_variable, add_offset);
- if (!a)
- return -ENOMEM;
-
- p = strempty(c->signature);
- for (i = 0, j = 0; i < c->n_offsets; i++) {
- unsigned k;
- size_t n;
-
- r = signature_element_length(p, &n);
- if (r < 0)
- return r;
- else {
- char t[n+1];
-
- memcpy(t, p, n);
- t[n] = 0;
-
- p += n;
-
- r = bus_gvariant_is_fixed_size(t);
- if (r < 0)
- return r;
- if (r > 0 || p[0] == 0)
- continue;
- }
-
- k = n_variable - 1 - j;
-
- write_word_le(a + k * sz, sz, c->offsets[i] - c->begin);
-
- j++;
- }
- }
-
- return 0;
-}
-
-_public_ int sd_bus_message_close_container(sd_bus_message *m) {
- struct bus_container *c;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(m->n_containers > 0, -EINVAL);
- assert_return(!m->poisoned, -ESTALE);
-
- c = message_get_container(m);
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- if (c->signature && c->signature[c->index] != 0)
- return -EINVAL;
-
- m->n_containers--;
-
- if (c->enclosing == SD_BUS_TYPE_ARRAY)
- r = bus_message_close_array(m, c);
- else if (c->enclosing == SD_BUS_TYPE_VARIANT)
- r = bus_message_close_variant(m, c);
- else if (c->enclosing == SD_BUS_TYPE_STRUCT || c->enclosing == SD_BUS_TYPE_DICT_ENTRY)
- r = bus_message_close_struct(m, c, true);
- else
- assert_not_reached("Unknown container type");
-
- free(c->signature);
- free(c->offsets);
-
- return r;
-}
-
-typedef struct {
- const char *types;
- unsigned n_struct;
- unsigned n_array;
-} TypeStack;
-
-static int type_stack_push(TypeStack *stack, unsigned max, unsigned *i, const char *types, unsigned n_struct, unsigned n_array) {
- assert(stack);
- assert(max > 0);
-
- if (*i >= max)
- return -EINVAL;
-
- stack[*i].types = types;
- stack[*i].n_struct = n_struct;
- stack[*i].n_array = n_array;
- (*i)++;
-
- return 0;
-}
-
-static int type_stack_pop(TypeStack *stack, unsigned max, unsigned *i, const char **types, unsigned *n_struct, unsigned *n_array) {
- assert(stack);
- assert(max > 0);
- assert(types);
- assert(n_struct);
- assert(n_array);
-
- if (*i <= 0)
- return 0;
-
- (*i)--;
- *types = stack[*i].types;
- *n_struct = stack[*i].n_struct;
- *n_array = stack[*i].n_array;
-
- return 1;
-}
-
-int bus_message_append_ap(
- sd_bus_message *m,
- const char *types,
- va_list ap) {
-
- unsigned n_array, n_struct;
- TypeStack stack[BUS_CONTAINER_DEPTH];
- unsigned stack_ptr = 0;
- int r;
-
- assert(m);
-
- if (!types)
- return 0;
-
- n_array = (unsigned) -1;
- n_struct = strlen(types);
-
- for (;;) {
- const char *t;
-
- if (n_array == 0 || (n_array == (unsigned) -1 && n_struct == 0)) {
- r = type_stack_pop(stack, ELEMENTSOF(stack), &stack_ptr, &types, &n_struct, &n_array);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return r;
-
- continue;
- }
-
- t = types;
- if (n_array != (unsigned) -1)
- n_array --;
- else {
- types ++;
- n_struct--;
- }
-
- switch (*t) {
-
- case SD_BUS_TYPE_BYTE: {
- uint8_t x;
-
- x = (uint8_t) va_arg(ap, int);
- r = sd_bus_message_append_basic(m, *t, &x);
- break;
- }
-
- case SD_BUS_TYPE_BOOLEAN:
- case SD_BUS_TYPE_INT32:
- case SD_BUS_TYPE_UINT32:
- case SD_BUS_TYPE_UNIX_FD: {
- uint32_t x;
-
- /* We assume a boolean is the same as int32_t */
- assert_cc(sizeof(int32_t) == sizeof(int));
-
- x = va_arg(ap, uint32_t);
- r = sd_bus_message_append_basic(m, *t, &x);
- break;
- }
-
- case SD_BUS_TYPE_INT16:
- case SD_BUS_TYPE_UINT16: {
- uint16_t x;
-
- x = (uint16_t) va_arg(ap, int);
- r = sd_bus_message_append_basic(m, *t, &x);
- break;
- }
-
- case SD_BUS_TYPE_INT64:
- case SD_BUS_TYPE_UINT64:
- case SD_BUS_TYPE_DOUBLE: {
- uint64_t x;
-
- x = va_arg(ap, uint64_t);
- r = sd_bus_message_append_basic(m, *t, &x);
- break;
- }
-
- case SD_BUS_TYPE_STRING:
- case SD_BUS_TYPE_OBJECT_PATH:
- case SD_BUS_TYPE_SIGNATURE: {
- const char *x;
-
- x = va_arg(ap, const char*);
- r = sd_bus_message_append_basic(m, *t, x);
- break;
- }
-
- case SD_BUS_TYPE_ARRAY: {
- size_t k;
-
- r = signature_element_length(t + 1, &k);
- if (r < 0)
- return r;
-
- {
- char s[k + 1];
- memcpy(s, t + 1, k);
- s[k] = 0;
-
- r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
- if (r < 0)
- return r;
- }
-
- if (n_array == (unsigned) -1) {
- types += k;
- n_struct -= k;
- }
-
- r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
- if (r < 0)
- return r;
-
- types = t + 1;
- n_struct = k;
- n_array = va_arg(ap, unsigned);
-
- break;
- }
-
- case SD_BUS_TYPE_VARIANT: {
- const char *s;
-
- s = va_arg(ap, const char*);
- if (!s)
- return -EINVAL;
-
- r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, s);
- if (r < 0)
- return r;
-
- r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
- if (r < 0)
- return r;
-
- types = s;
- n_struct = strlen(s);
- n_array = (unsigned) -1;
-
- break;
- }
-
- case SD_BUS_TYPE_STRUCT_BEGIN:
- case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
- size_t k;
-
- r = signature_element_length(t, &k);
- if (r < 0)
- return r;
-
- {
- char s[k - 1];
-
- memcpy(s, t + 1, k - 2);
- s[k - 2] = 0;
-
- r = sd_bus_message_open_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
- if (r < 0)
- return r;
- }
-
- if (n_array == (unsigned) -1) {
- types += k - 1;
- n_struct -= k - 1;
- }
-
- r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
- if (r < 0)
- return r;
-
- types = t + 1;
- n_struct = k - 2;
- n_array = (unsigned) -1;
-
- break;
- }
-
- default:
- r = -EINVAL;
- }
-
- if (r < 0)
- return r;
- }
-
- return 1;
-}
-
-_public_ int sd_bus_message_append(sd_bus_message *m, const char *types, ...) {
- va_list ap;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(types, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(!m->poisoned, -ESTALE);
-
- va_start(ap, types);
- r = bus_message_append_ap(m, types, ap);
- va_end(ap);
-
- return r;
-}
-
-_public_ int sd_bus_message_append_array_space(
- sd_bus_message *m,
- char type,
- size_t size,
- void **ptr) {
-
- ssize_t align, sz;
- void *a;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(bus_type_is_trivial(type) && type != SD_BUS_TYPE_BOOLEAN, -EINVAL);
- assert_return(ptr || size == 0, -EINVAL);
- assert_return(!m->poisoned, -ESTALE);
-
- /* alignment and size of the trivial types (except bool) is
- * identical for gvariant and dbus1 marshalling */
- align = bus_type_get_alignment(type);
- sz = bus_type_get_size(type);
-
- assert_se(align > 0);
- assert_se(sz > 0);
-
- if (size % sz != 0)
- return -EINVAL;
-
- r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type));
- if (r < 0)
- return r;
-
- a = message_extend_body(m, align, size, false);
- if (!a)
- return -ENOMEM;
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return r;
-
- *ptr = a;
- return 0;
-}
-
-_public_ int sd_bus_message_append_array(sd_bus_message *m,
- char type,
- const void *ptr,
- size_t size) {
- int r;
- void *p;
-
- assert_return(m, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(bus_type_is_trivial(type), -EINVAL);
- assert_return(ptr || size == 0, -EINVAL);
- assert_return(!m->poisoned, -ESTALE);
-
- r = sd_bus_message_append_array_space(m, type, size, &p);
- if (r < 0)
- return r;
-
- if (size > 0)
- memcpy(p, ptr, size);
-
- return 0;
-}
-
-_public_ int sd_bus_message_append_array_iovec(
- sd_bus_message *m,
- char type,
- const struct iovec *iov,
- unsigned n) {
-
- size_t size;
- unsigned i;
- void *p;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(bus_type_is_trivial(type), -EINVAL);
- assert_return(iov || n == 0, -EINVAL);
- assert_return(!m->poisoned, -ESTALE);
-
- size = IOVEC_TOTAL_SIZE(iov, n);
-
- r = sd_bus_message_append_array_space(m, type, size, &p);
- if (r < 0)
- return r;
-
- for (i = 0; i < n; i++) {
-
- if (iov[i].iov_base)
- memcpy(p, iov[i].iov_base, iov[i].iov_len);
- else
- memset(p, 0, iov[i].iov_len);
-
- p = (uint8_t*) p + iov[i].iov_len;
- }
-
- return 0;
-}
-
-_public_ int sd_bus_message_append_array_memfd(sd_bus_message *m,
- char type,
- sd_memfd *memfd) {
- _cleanup_close_ int copy_fd = -1;
- struct bus_body_part *part;
- ssize_t align, sz;
- uint64_t size;
- void *a;
- int r;
-
- if (!m)
- return -EINVAL;
- if (!memfd)
- return -EINVAL;
- if (m->sealed)
- return -EPERM;
- if (!bus_type_is_trivial(type))
- return -EINVAL;
- if (m->poisoned)
- return -ESTALE;
-
- r = sd_memfd_set_sealed(memfd, true);
- if (r < 0)
- return r;
-
- copy_fd = sd_memfd_dup_fd(memfd);
- if (copy_fd < 0)
- return copy_fd;
-
- r = sd_memfd_get_size(memfd, &size);
- if (r < 0)
- return r;
-
- align = bus_type_get_alignment(type);
- sz = bus_type_get_size(type);
-
- assert_se(align > 0);
- assert_se(sz > 0);
-
- if (size % sz != 0)
- return -EINVAL;
-
- if (size > (uint64_t) (uint32_t) -1)
- return -EINVAL;
-
- r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type));
- if (r < 0)
- return r;
-
- a = message_extend_body(m, align, 0, false);
- if (!a)
- return -ENOMEM;
-
- part = message_append_part(m);
- if (!part)
- return -ENOMEM;
-
- part->memfd = copy_fd;
- part->sealed = true;
- part->size = size;
- copy_fd = -1;
-
- m->header->body_size += size;
- message_extend_containers(m, size);
-
- return sd_bus_message_close_container(m);
-}
-
-_public_ int sd_bus_message_append_string_memfd(sd_bus_message *m, sd_memfd *memfd) {
- _cleanup_close_ int copy_fd = -1;
- struct bus_body_part *part;
- struct bus_container *c;
- uint64_t size;
- void *a;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(memfd, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(!m->poisoned, -ESTALE);
-
- r = sd_memfd_set_sealed(memfd, true);
- if (r < 0)
- return r;
-
- copy_fd = sd_memfd_dup_fd(memfd);
- if (copy_fd < 0)
- return copy_fd;
-
- r = sd_memfd_get_size(memfd, &size);
- if (r < 0)
- return r;
-
- /* We require this to be NUL terminated */
- if (size == 0)
- return -EINVAL;
-
- if (size > (uint64_t) (uint32_t) -1)
- return -EINVAL;
-
- c = message_get_container(m);
- if (c->signature && c->signature[c->index]) {
- /* Container signature is already set */
-
- if (c->signature[c->index] != SD_BUS_TYPE_STRING)
- return -ENXIO;
- } else {
- char *e;
-
- /* Maybe we can append to the signature? But only if this is the top-level container*/
- if (c->enclosing != 0)
- return -ENXIO;
-
- e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRING), NULL);
- if (!e) {
- m->poisoned = true;
- return -ENOMEM;
- }
- }
-
- if (!BUS_MESSAGE_IS_GVARIANT(m)) {
- a = message_extend_body(m, 4, 4, false);
- if (!a)
- return -ENOMEM;
-
- *(uint32_t*) a = size - 1;
- }
-
- part = message_append_part(m);
- if (!part)
- return -ENOMEM;
-
- part->memfd = copy_fd;
- part->sealed = true;
- part->size = size;
- copy_fd = -1;
-
- m->header->body_size += size;
- message_extend_containers(m, size);
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- r = message_add_offset(m, m->header->body_size);
- if (r < 0) {
- m->poisoned = true;
- return -ENOMEM;
- }
- }
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- c->index++;
-
- return 0;
-}
-
-_public_ int sd_bus_message_append_strv(sd_bus_message *m, char **l) {
- char **i;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(!m->poisoned, -ESTALE);
-
- r = sd_bus_message_open_container(m, 'a', "s");
- if (r < 0)
- return r;
-
- STRV_FOREACH(i, l) {
- r = sd_bus_message_append_basic(m, 's', *i);
- if (r < 0)
- return r;
- }
-
- return sd_bus_message_close_container(m);
-}
-
-static int bus_message_close_header(sd_bus_message *m) {
- uint8_t *a;
- size_t sz, i;
-
- assert(m);
-
- if (!BUS_MESSAGE_IS_GVARIANT(m))
- return 0;
-
- if (m->n_header_offsets < 1)
- return 0;
-
- assert(m->header->fields_size == m->header_offsets[m->n_header_offsets-1]);
-
- sz = determine_word_size(m->header->fields_size, m->n_header_offsets);
-
- a = message_extend_fields(m, 1, sz * m->n_header_offsets, false);
- if (!a)
- return -ENOMEM;
-
- for (i = 0; i < m->n_header_offsets; i++)
- write_word_le(a + sz*i, sz, m->header_offsets[i]);
-
- return 0;
-}
-
-int bus_message_seal(sd_bus_message *m, uint64_t cookie, usec_t timeout) {
- struct bus_body_part *part;
- size_t l, a;
- unsigned i;
- int r;
-
- assert(m);
-
- if (m->sealed)
- return -EPERM;
-
- if (m->n_containers > 0)
- return -EBADMSG;
-
- if (m->poisoned)
- return -ESTALE;
-
- /* In vtables the return signature of method calls is listed,
- * let's check if they match if this is a response */
- if (m->header->type == SD_BUS_MESSAGE_METHOD_RETURN &&
- m->enforced_reply_signature &&
- !streq(strempty(m->root_container.signature), m->enforced_reply_signature))
- return -ENOMSG;
-
- /* If gvariant marshalling is used we need to close the body structure */
- r = bus_message_close_struct(m, &m->root_container, false);
- if (r < 0)
- return r;
-
- /* If there's a non-trivial signature set, then add it in here */
- if (!isempty(m->root_container.signature)) {
- r = message_append_field_signature(m, BUS_MESSAGE_HEADER_SIGNATURE, m->root_container.signature, NULL);
- if (r < 0)
- return r;
- }
-
- if (m->n_fds > 0) {
- r = message_append_field_uint32(m, BUS_MESSAGE_HEADER_UNIX_FDS, m->n_fds);
- if (r < 0)
- return r;
- }
-
- r = bus_message_close_header(m);
- if (r < 0)
- return r;
-
- m->header->serial = (uint32_t) cookie;
- m->timeout = m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED ? 0 : timeout;
-
- /* Add padding at the end of the fields part, since we know
- * the body needs to start at an 8 byte alignment. We made
- * sure we allocated enough space for this, so all we need to
- * do here is to zero it out. */
- l = BUS_MESSAGE_FIELDS_SIZE(m);
- a = ALIGN8(l) - l;
- if (a > 0)
- memset((uint8_t*) BUS_MESSAGE_FIELDS(m) + l, 0, a);
-
- /* If this is something we can send as memfd, then let's seal
- the memfd now. Note that we can send memfds as payload only
- for directed messages, and not for broadcasts. */
- if (m->destination && m->bus && m->bus->use_memfd) {
- MESSAGE_FOREACH_PART(part, i, m)
- if (part->memfd >= 0 && !part->sealed && (part->size > MEMFD_MIN_SIZE || m->bus->use_memfd < 0)) {
- uint64_t sz;
-
- /* Try to seal it if that makes
- * sense. First, unmap our own map to
- * make sure we don't keep it busy. */
- bus_body_part_unmap(part);
-
- /* Then, sync up real memfd size */
- sz = part->size;
- if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &sz) < 0)
- return -errno;
-
- /* Finally, try to seal */
- if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1) >= 0)
- part->sealed = true;
- }
- }
-
- m->root_container.end = BUS_MESSAGE_BODY_SIZE(m);
- m->root_container.index = 0;
- m->root_container.offset_index = 0;
- m->root_container.item_size = m->root_container.n_offsets > 0 ? m->root_container.offsets[0] : 0;
-
- m->sealed = true;
-
- return 0;
-}
-
-int bus_body_part_map(struct bus_body_part *part) {
- void *p;
- size_t psz;
-
- assert_se(part);
-
- if (part->data)
- return 0;
-
- if (part->size <= 0)
- return 0;
-
- /* For smaller zero parts (as used for padding) we don't need to map anything... */
- if (part->memfd < 0 && part->is_zero && part->size < 8) {
- static const uint8_t zeroes[7] = { };
- part->data = (void*) zeroes;
- return 0;
- }
-
- psz = PAGE_ALIGN(part->size);
-
- if (part->memfd >= 0)
- p = mmap(NULL, psz, PROT_READ, MAP_SHARED, part->memfd, 0);
- else if (part->is_zero)
- p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
- else
- return -EINVAL;
-
- if (p == MAP_FAILED)
- return -errno;
-
- part->mapped = psz;
- part->data = p;
- part->munmap_this = true;
-
- return 0;
-}
-
-void bus_body_part_unmap(struct bus_body_part *part) {
-
- assert_se(part);
-
- if (part->memfd < 0)
- return;
-
- if (!part->data)
- return;
-
- if (!part->munmap_this)
- return;
-
- assert_se(munmap(part->data, part->mapped) == 0);
-
- part->data = NULL;
- part->mapped = 0;
- part->munmap_this = false;
-
- return;
-}
-
-static int buffer_peek(const void *p, uint32_t sz, size_t *rindex, size_t align, size_t nbytes, void **r) {
- size_t k, start, end;
-
- assert(rindex);
- assert(align > 0);
-
- start = ALIGN_TO((size_t) *rindex, align);
- end = start + nbytes;
-
- if (end > sz)
- return -EBADMSG;
-
- /* Verify that padding is 0 */
- for (k = *rindex; k < start; k++)
- if (((const uint8_t*) p)[k] != 0)
- return -EBADMSG;
-
- if (r)
- *r = (uint8_t*) p + start;
-
- *rindex = end;
-
- return 1;
-}
-
-static bool message_end_of_signature(sd_bus_message *m) {
- struct bus_container *c;
-
- assert(m);
-
- c = message_get_container(m);
- return !c->signature || c->signature[c->index] == 0;
-}
-
-static bool message_end_of_array(sd_bus_message *m, size_t index) {
- struct bus_container *c;
-
- assert(m);
-
- c = message_get_container(m);
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- return false;
-
- if (BUS_MESSAGE_IS_GVARIANT(m))
- return index >= c->end;
- else {
- assert(c->array_size);
- return index >= c->begin + BUS_MESSAGE_BSWAP32(m, *c->array_size);
- }
-}
-
-_public_ int sd_bus_message_at_end(sd_bus_message *m, int complete) {
- assert_return(m, -EINVAL);
- assert_return(m->sealed, -EPERM);
-
- if (complete && m->n_containers > 0)
- return false;
-
- if (message_end_of_signature(m))
- return true;
-
- if (message_end_of_array(m, m->rindex))
- return true;
-
- return false;
-}
-
-static struct bus_body_part* find_part(sd_bus_message *m, size_t index, size_t sz, void **p) {
- struct bus_body_part *part;
- size_t begin;
- int r;
-
- assert(m);
-
- if (m->cached_rindex_part && index >= m->cached_rindex_part_begin) {
- part = m->cached_rindex_part;
- begin = m->cached_rindex_part_begin;
- } else {
- part = &m->body;
- begin = 0;
- }
-
- while (part) {
- if (index < begin)
- return NULL;
-
- if (index + sz <= begin + part->size) {
-
- r = bus_body_part_map(part);
- if (r < 0)
- return NULL;
-
- if (p)
- *p = (uint8_t*) part->data + index - begin;
-
- m->cached_rindex_part = part;
- m->cached_rindex_part_begin = begin;
-
- return part;
- }
-
- begin += part->size;
- part = part->next;
- }
-
- return NULL;
-}
-
-static int container_next_item(sd_bus_message *m, struct bus_container *c, size_t *rindex) {
- int r;
-
- assert(m);
- assert(c);
- assert(rindex);
-
- if (!BUS_MESSAGE_IS_GVARIANT(m))
- return 0;
-
- if (c->enclosing == SD_BUS_TYPE_ARRAY) {
- int sz;
-
- sz = bus_gvariant_get_size(c->signature);
- if (sz < 0) {
- int alignment;
-
- if (c->offset_index+1 >= c->n_offsets)
- goto end;
-
- /* Variable-size array */
-
- alignment = bus_gvariant_get_alignment(c->signature);
- assert(alignment > 0);
-
- *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment);
- c->item_size = c->offsets[c->offset_index+1] - *rindex;
- } else {
-
- if (c->offset_index+1 >= (c->end-c->begin)/sz)
- goto end;
-
- /* Fixed-size array */
- *rindex = c->begin + (c->offset_index+1) * sz;
- c->item_size = sz;
- }
-
- c->offset_index++;
-
- } else if (c->enclosing == 0 ||
- c->enclosing == SD_BUS_TYPE_STRUCT ||
- c->enclosing == SD_BUS_TYPE_DICT_ENTRY) {
-
- int alignment;
- size_t n, j;
-
- if (c->offset_index+1 >= c->n_offsets)
- goto end;
-
- r = signature_element_length(c->signature + c->index, &n);
- if (r < 0)
- return r;
-
- r = signature_element_length(c->signature + c->index + n, &j);
- if (r < 0)
- return r;
- else {
- char t[j+1];
- memcpy(t, c->signature + c->index + n, j);
- t[j] = 0;
-
- alignment = bus_gvariant_get_alignment(t);
- }
-
- assert(alignment > 0);
-
- *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment);
- c->item_size = c->offsets[c->offset_index+1] - *rindex;
-
- c->offset_index++;
-
- } else if (c->enclosing == SD_BUS_TYPE_VARIANT)
- goto end;
- else
- assert_not_reached("Unknown container type");
-
- return 0;
-
-end:
- /* Reached the end */
- *rindex = c->end;
- c->item_size = 0;
- return 0;
-}
-
-
-static int message_peek_body(
- sd_bus_message *m,
- size_t *rindex,
- size_t align,
- size_t nbytes,
- void **ret) {
-
- size_t k, start, end, padding;
- struct bus_body_part *part;
- uint8_t *q;
-
- assert(m);
- assert(rindex);
- assert(align > 0);
-
- start = ALIGN_TO((size_t) *rindex, align);
- padding = start - *rindex;
- end = start + nbytes;
-
- if (end > BUS_MESSAGE_BODY_SIZE(m))
- return -EBADMSG;
-
- part = find_part(m, *rindex, padding, (void**) &q);
- if (!part)
- return -EBADMSG;
-
- if (q) {
- /* Verify padding */
- for (k = 0; k < padding; k++)
- if (q[k] != 0)
- return -EBADMSG;
- }
-
- part = find_part(m, start, nbytes, (void**) &q);
- if (!part || (nbytes > 0 && !q))
- return -EBADMSG;
-
- *rindex = end;
-
- if (ret)
- *ret = q;
-
- return 0;
-}
-
-static bool validate_nul(const char *s, size_t l) {
-
- /* Check for NUL chars in the string */
- if (memchr(s, 0, l))
- return false;
-
- /* Check for NUL termination */
- if (s[l] != 0)
- return false;
-
- return true;
-}
-
-static bool validate_string(const char *s, size_t l) {
-
- if (!validate_nul(s, l))
- return false;
-
- /* Check if valid UTF8 */
- if (!utf8_is_valid(s))
- return false;
-
- return true;
-}
-
-static bool validate_signature(const char *s, size_t l) {
-
- if (!validate_nul(s, l))
- return false;
-
- /* Check if valid signature */
- if (!signature_is_valid(s, true))
- return false;
-
- return true;
-}
-
-static bool validate_object_path(const char *s, size_t l) {
-
- if (!validate_nul(s, l))
- return false;
-
- if (!object_path_is_valid(s))
- return false;
-
- return true;
-}
-
-_public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
- struct bus_container *c;
- size_t rindex;
- void *q;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(m->sealed, -EPERM);
- assert_return(bus_type_is_basic(type), -EINVAL);
-
- if (message_end_of_signature(m))
- return -ENXIO;
-
- if (message_end_of_array(m, m->rindex))
- return 0;
-
- c = message_get_container(m);
- if (c->signature[c->index] != type)
- return -ENXIO;
-
- rindex = m->rindex;
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
-
- if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) {
- bool ok;
-
- r = message_peek_body(m, &rindex, 1, c->item_size, &q);
- if (r < 0)
- return r;
-
- if (type == SD_BUS_TYPE_STRING)
- ok = validate_string(q, c->item_size-1);
- else if (type == SD_BUS_TYPE_OBJECT_PATH)
- ok = validate_object_path(q, c->item_size-1);
- else
- ok = validate_signature(q, c->item_size-1);
-
- if (!ok)
- return -EBADMSG;
-
- if (p)
- *(const char**) p = q;
- } else {
- int sz, align;
-
- sz = bus_gvariant_get_size(CHAR_TO_STR(type));
- assert(sz > 0);
- if ((size_t) sz != c->item_size)
- return -EBADMSG;
-
- align = bus_gvariant_get_alignment(CHAR_TO_STR(type));
- assert(align > 0);
-
- r = message_peek_body(m, &rindex, align, c->item_size, &q);
- if (r < 0)
- return r;
-
- switch (type) {
-
- case SD_BUS_TYPE_BYTE:
- if (p)
- *(uint8_t*) p = *(uint8_t*) q;
- break;
-
- case SD_BUS_TYPE_BOOLEAN:
- if (p)
- *(int*) p = !!*(uint8_t*) q;
- break;
-
- case SD_BUS_TYPE_INT16:
- case SD_BUS_TYPE_UINT16:
- if (p)
- *(uint16_t*) p = BUS_MESSAGE_BSWAP16(m, *(uint16_t*) q);
- break;
-
- case SD_BUS_TYPE_INT32:
- case SD_BUS_TYPE_UINT32:
- if (p)
- *(uint32_t*) p = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
- break;
-
- case SD_BUS_TYPE_INT64:
- case SD_BUS_TYPE_UINT64:
- case SD_BUS_TYPE_DOUBLE:
- if (p)
- *(uint64_t*) p = BUS_MESSAGE_BSWAP64(m, *(uint64_t*) q);
- break;
-
- case SD_BUS_TYPE_UNIX_FD: {
- uint32_t j;
-
- j = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
- if (j >= m->n_fds)
- return -EBADMSG;
-
- if (p)
- *(int*) p = m->fds[j];
-
- break;
- }
-
- default:
- assert_not_reached("unexpected type");
- }
- }
-
- r = container_next_item(m, c, &rindex);
- if (r < 0)
- return r;
- } else {
-
- rindex = m->rindex;
-
- if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH)) {
- uint32_t l;
- bool ok;
-
- r = message_peek_body(m, &rindex, 4, 4, &q);
- if (r < 0)
- return r;
-
- l = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
- r = message_peek_body(m, &rindex, 1, l+1, &q);
- if (r < 0)
- return r;
-
- if (type == SD_BUS_TYPE_OBJECT_PATH)
- ok = validate_object_path(q, l);
- else
- ok = validate_string(q, l);
- if (!ok)
- return -EBADMSG;
-
- if (p)
- *(const char**) p = q;
-
- } else if (type == SD_BUS_TYPE_SIGNATURE) {
- uint8_t l;
-
- r = message_peek_body(m, &rindex, 1, 1, &q);
- if (r < 0)
- return r;
-
- l = *(uint8_t*) q;
- r = message_peek_body(m, &rindex, 1, l+1, &q);
- if (r < 0)
- return r;
-
- if (!validate_signature(q, l))
- return -EBADMSG;
-
- if (p)
- *(const char**) p = q;
-
- } else {
- ssize_t sz, align;
-
- align = bus_type_get_alignment(type);
- assert(align > 0);
-
- sz = bus_type_get_size(type);
- assert(sz > 0);
-
- r = message_peek_body(m, &rindex, align, sz, &q);
- if (r < 0)
- return r;
-
- switch (type) {
-
- case SD_BUS_TYPE_BYTE:
- if (p)
- *(uint8_t*) p = *(uint8_t*) q;
- break;
-
- case SD_BUS_TYPE_BOOLEAN:
- if (p)
- *(int*) p = !!*(uint32_t*) q;
- break;
-
- case SD_BUS_TYPE_INT16:
- case SD_BUS_TYPE_UINT16:
- if (p)
- *(uint16_t*) p = BUS_MESSAGE_BSWAP16(m, *(uint16_t*) q);
- break;
-
- case SD_BUS_TYPE_INT32:
- case SD_BUS_TYPE_UINT32:
- if (p)
- *(uint32_t*) p = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
- break;
-
- case SD_BUS_TYPE_INT64:
- case SD_BUS_TYPE_UINT64:
- case SD_BUS_TYPE_DOUBLE:
- if (p)
- *(uint64_t*) p = BUS_MESSAGE_BSWAP64(m, *(uint64_t*) q);
- break;
-
- case SD_BUS_TYPE_UNIX_FD: {
- uint32_t j;
-
- j = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
- if (j >= m->n_fds)
- return -EBADMSG;
-
- if (p)
- *(int*) p = m->fds[j];
- break;
- }
-
- default:
- assert_not_reached("Unknown basic type...");
- }
- }
- }
-
- m->rindex = rindex;
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- c->index++;
-
- return 1;
-}
-
-static int bus_message_enter_array(
- sd_bus_message *m,
- struct bus_container *c,
- const char *contents,
- uint32_t **array_size,
- size_t *item_size,
- size_t **offsets,
- size_t *n_offsets) {
-
- size_t rindex;
- void *q;
- int r, alignment;
-
- assert(m);
- assert(c);
- assert(contents);
- assert(array_size);
- assert(item_size);
- assert(offsets);
- assert(n_offsets);
-
- if (!signature_is_single(contents, true))
- return -EINVAL;
-
- if (!c->signature || c->signature[c->index] == 0)
- return -ENXIO;
-
- if (c->signature[c->index] != SD_BUS_TYPE_ARRAY)
- return -ENXIO;
-
- if (!startswith(c->signature + c->index + 1, contents))
- return -ENXIO;
-
- rindex = m->rindex;
-
- if (!BUS_MESSAGE_IS_GVARIANT(m)) {
- /* dbus1 */
-
- r = message_peek_body(m, &rindex, 4, 4, &q);
- if (r < 0)
- return r;
-
- if (BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q) > BUS_ARRAY_MAX_SIZE)
- return -EBADMSG;
-
- alignment = bus_type_get_alignment(contents[0]);
- if (alignment < 0)
- return alignment;
-
- r = message_peek_body(m, &rindex, alignment, 0, NULL);
- if (r < 0)
- return r;
-
- *array_size = (uint32_t*) q;
-
- } else if (c->item_size <= 0) {
-
- /* gvariant: empty array */
- *item_size = 0;
- *offsets = NULL;
- *n_offsets = 0;
-
- } else if (bus_gvariant_is_fixed_size(contents)) {
-
- /* gvariant: fixed length array */
- *item_size = bus_gvariant_get_size(contents);
- *offsets = NULL;
- *n_offsets = 0;
-
- } else {
- size_t where, p = 0, framing, sz;
- unsigned i;
-
- /* gvariant: variable length array */
- sz = determine_word_size(c->item_size, 0);
-
- where = rindex + c->item_size - sz;
- r = message_peek_body(m, &where, 1, sz, &q);
- if (r < 0)
- return r;
-
- framing = read_word_le(q, sz);
- if (framing > c->item_size - sz)
- return -EBADMSG;
- if ((c->item_size - framing) % sz != 0)
- return -EBADMSG;
-
- *n_offsets = (c->item_size - framing) / sz;
-
- where = rindex + framing;
- r = message_peek_body(m, &where, 1, *n_offsets * sz, &q);
- if (r < 0)
- return r;
-
- *offsets = new(size_t, *n_offsets);
- if (!*offsets)
- return -ENOMEM;
-
- for (i = 0; i < *n_offsets; i++) {
- size_t x;
-
- x = read_word_le((uint8_t*) q + i * sz, sz);
- if (x > c->item_size - sz)
- return -EBADMSG;
- if (x < p)
- return -EBADMSG;
-
- (*offsets)[i] = rindex + x;
- p = x;
- }
-
- *item_size = (*offsets)[0] - rindex;
- }
-
- m->rindex = rindex;
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- c->index += 1 + strlen(contents);
-
- return 1;
-}
-
-static int bus_message_enter_variant(
- sd_bus_message *m,
- struct bus_container *c,
- const char *contents,
- size_t *item_size) {
-
- size_t rindex;
- uint8_t l;
- void *q;
- int r;
-
- assert(m);
- assert(c);
- assert(contents);
- assert(item_size);
-
- if (!signature_is_single(contents, false))
- return -EINVAL;
-
- if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN)
- return -EINVAL;
-
- if (!c->signature || c->signature[c->index] == 0)
- return -ENXIO;
-
- if (c->signature[c->index] != SD_BUS_TYPE_VARIANT)
- return -ENXIO;
-
- rindex = m->rindex;
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- size_t k, where;
-
- k = strlen(contents);
- if (1+k > c->item_size)
- return -EBADMSG;
-
- where = rindex + c->item_size - (1+k);
- r = message_peek_body(m, &where, 1, 1+k, &q);
- if (r < 0)
- return r;
-
- if (*(char*) q != 0)
- return -EBADMSG;
-
- if (memcmp((uint8_t*) q+1, contents, k))
- return -ENXIO;
-
- *item_size = c->item_size - (1+k);
-
- } else {
- r = message_peek_body(m, &rindex, 1, 1, &q);
- if (r < 0)
- return r;
-
- l = *(uint8_t*) q;
- r = message_peek_body(m, &rindex, 1, l+1, &q);
- if (r < 0)
- return r;
-
- if (!validate_signature(q, l))
- return -EBADMSG;
-
- if (!streq(q, contents))
- return -ENXIO;
- }
-
- m->rindex = rindex;
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- c->index++;
-
- return 1;
-}
-
-static int build_struct_offsets(
- sd_bus_message *m,
- const char *signature,
- size_t size,
- size_t *item_size,
- size_t **offsets,
- size_t *n_offsets) {
-
- unsigned n_variable = 0, n_total = 0, v;
- size_t previous = 0, where;
- const char *p;
- size_t sz;
- void *q;
- int r;
-
- assert(m);
- assert(item_size);
- assert(offsets);
- assert(n_offsets);
-
- if (isempty(signature)) {
- *item_size = 0;
- *offsets = NULL;
- *n_offsets = 0;
- return 0;
- }
-
- sz = determine_word_size(size, 0);
- if (sz <= 0)
- return -EBADMSG;
-
- /* First, loop over signature and count variable elements and
- * elements in general. We use this to know how large the
- * offset array is at the end of the structure. Note that
- * GVariant only stores offsets for all variable size elements
- * that are not the last item. */
-
- p = signature;
- while (*p != 0) {
- size_t n;
-
- r = signature_element_length(p, &n);
- if (r < 0)
- return r;
- else {
- char t[n+1];
-
- memcpy(t, p, n);
- t[n] = 0;
-
- r = bus_gvariant_is_fixed_size(t);
- }
-
- if (r < 0)
- return r;
- if (r == 0 && p[n] != 0) /* except the last item */
- n_variable ++;
- n_total++;
-
- p += n;
- }
-
- if (size < n_variable * sz)
- return -EBADMSG;
-
- where = m->rindex + size - (n_variable * sz);
- r = message_peek_body(m, &where, 1, n_variable * sz, &q);
- if (r < 0)
- return r;
-
- v = n_variable;
-
- *offsets = new(size_t, n_total);
- if (!*offsets)
- return -ENOMEM;
-
- *n_offsets = 0;
-
- /* Second, loop again and build an offset table */
- p = signature;
- while (*p != 0) {
- size_t n, offset;
- int k;
-
- r = signature_element_length(p, &n);
- if (r < 0)
- return r;
- else {
- char t[n+1];
-
- memcpy(t, p, n);
- t[n] = 0;
-
- k = bus_gvariant_get_size(t);
- if (k < 0) {
- size_t x;
-
- /* variable size */
- if (v > 0) {
- v--;
-
- x = read_word_le((uint8_t*) q + v*sz, sz);
- if (x >= size)
- return -EBADMSG;
- if (m->rindex + x < previous)
- return -EBADMSG;
- } else
- /* The last item's end
- * is determined from
- * the start of the
- * offset array */
- x = size - (n_variable * sz);
-
- offset = m->rindex + x;
-
- } else {
- size_t align;
-
- /* fixed size */
- align = bus_gvariant_get_alignment(t);
- assert(align > 0);
-
- offset = (*n_offsets == 0 ? m->rindex : ALIGN_TO((*offsets)[*n_offsets-1], align)) + k;
- }
- }
-
- previous = (*offsets)[(*n_offsets)++] = offset;
- p += n;
- }
-
- assert(v == 0);
- assert(*n_offsets == n_total);
-
- *item_size = (*offsets)[0] - m->rindex;
- return 0;
-}
-
-static int enter_struct_or_dict_entry(
- sd_bus_message *m,
- struct bus_container *c,
- const char *contents,
- size_t *item_size,
- size_t **offsets,
- size_t *n_offsets) {
-
- int r;
-
- assert(m);
- assert(c);
- assert(contents);
- assert(item_size);
- assert(offsets);
- assert(n_offsets);
-
- if (!BUS_MESSAGE_IS_GVARIANT(m)) {
-
- /* dbus1 */
- r = message_peek_body(m, &m->rindex, 8, 0, NULL);
- if (r < 0)
- return r;
-
- } else if (c->item_size <= 0) {
-
- /* gvariant empty struct */
- *item_size = 0;
- *offsets = NULL;
- *n_offsets = 0;
- } else
- /* gvariant with contents */
- return build_struct_offsets(m, contents, c->item_size, item_size, offsets, n_offsets);
-
- return 0;
-}
-
-static int bus_message_enter_struct(
- sd_bus_message *m,
- struct bus_container *c,
- const char *contents,
- size_t *item_size,
- size_t **offsets,
- size_t *n_offsets) {
-
- size_t l;
- int r;
-
- assert(m);
- assert(c);
- assert(contents);
- assert(item_size);
- assert(offsets);
- assert(n_offsets);
-
- if (!signature_is_valid(contents, false))
- return -EINVAL;
-
- if (!c->signature || c->signature[c->index] == 0)
- return -ENXIO;
-
- l = strlen(contents);
-
- if (c->signature[c->index] != SD_BUS_TYPE_STRUCT_BEGIN ||
- !startswith(c->signature + c->index + 1, contents) ||
- c->signature[c->index + 1 + l] != SD_BUS_TYPE_STRUCT_END)
- return -ENXIO;
-
- r = enter_struct_or_dict_entry(m, c, contents, item_size, offsets, n_offsets);
- if (r < 0)
- return r;
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- c->index += 1 + l + 1;
-
- return 1;
-}
-
-static int bus_message_enter_dict_entry(
- sd_bus_message *m,
- struct bus_container *c,
- const char *contents,
- size_t *item_size,
- size_t **offsets,
- size_t *n_offsets) {
-
- size_t l;
- int r;
-
- assert(m);
- assert(c);
- assert(contents);
-
- if (!signature_is_pair(contents))
- return -EINVAL;
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- return -ENXIO;
-
- if (!c->signature || c->signature[c->index] == 0)
- return 0;
-
- l = strlen(contents);
-
- if (c->signature[c->index] != SD_BUS_TYPE_DICT_ENTRY_BEGIN ||
- !startswith(c->signature + c->index + 1, contents) ||
- c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END)
- return -ENXIO;
-
- r = enter_struct_or_dict_entry(m, c, contents, item_size, offsets, n_offsets);
- if (r < 0)
- return r;
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY)
- c->index += 1 + l + 1;
-
- return 1;
-}
-
-_public_ int sd_bus_message_enter_container(sd_bus_message *m,
- char type,
- const char *contents) {
- struct bus_container *c, *w;
- uint32_t *array_size = NULL;
- char *signature;
- size_t before;
- size_t *offsets = NULL;
- size_t n_offsets = 0, item_size = 0;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(m->sealed, -EPERM);
- assert_return(type != 0 || !contents, -EINVAL);
-
- if (type == 0 || !contents) {
- const char *cc;
- char tt;
-
- /* Allow entering into anonymous containers */
- r = sd_bus_message_peek_type(m, &tt, &cc);
- if (r < 0)
- return r;
-
- if (type != 0 && type != tt)
- return -ENXIO;
-
- if (contents && !streq(contents, cc))
- return -ENXIO;
-
- type = tt;
- contents = cc;
- }
-
- /*
- * We enforce a global limit on container depth, that is much
- * higher than the 32 structs and 32 arrays the specification
- * mandates. This is simpler to implement for us, and we need
- * this only to ensure our container array doesn't grow
- * without bounds. We are happy to return any data from a
- * message as long as the data itself is valid, even if the
- * overall message might be not.
- *
- * Note that the message signature is validated when
- * parsing the headers, and that validation does check the
- * 32/32 limit.
- *
- * Note that the specification defines no limits on the depth
- * of stacked variants, but we do.
- */
- if (m->n_containers >= BUS_CONTAINER_DEPTH)
- return -EBADMSG;
-
- if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1))
- return -ENOMEM;
-
- if (message_end_of_signature(m))
- return -ENXIO;
-
- if (message_end_of_array(m, m->rindex))
- return 0;
-
- c = message_get_container(m);
-
- signature = strdup(contents);
- if (!signature)
- return -ENOMEM;
-
- c->saved_index = c->index;
- before = m->rindex;
-
- if (type == SD_BUS_TYPE_ARRAY)
- r = bus_message_enter_array(m, c, contents, &array_size, &item_size, &offsets, &n_offsets);
- else if (type == SD_BUS_TYPE_VARIANT)
- r = bus_message_enter_variant(m, c, contents, &item_size);
- else if (type == SD_BUS_TYPE_STRUCT)
- r = bus_message_enter_struct(m, c, contents, &item_size, &offsets, &n_offsets);
- else if (type == SD_BUS_TYPE_DICT_ENTRY)
- r = bus_message_enter_dict_entry(m, c, contents, &item_size, &offsets, &n_offsets);
- else
- r = -EINVAL;
-
- if (r <= 0) {
- free(signature);
- free(offsets);
- return r;
- }
-
- /* OK, let's fill it in */
- w = m->containers + m->n_containers++;
- w->enclosing = type;
- w->signature = signature;
- w->peeked_signature = NULL;
- w->index = 0;
-
- w->before = before;
- w->begin = m->rindex;
- w->end = m->rindex + c->item_size;
-
- w->array_size = array_size;
- w->item_size = item_size;
- w->offsets = offsets;
- w->n_offsets = n_offsets;
- w->offset_index = 0;
-
- return 1;
-}
-
-_public_ int sd_bus_message_exit_container(sd_bus_message *m) {
- struct bus_container *c;
- unsigned saved;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(m->sealed, -EPERM);
- assert_return(m->n_containers > 0, -ENXIO);
-
- c = message_get_container(m);
-
- if (c->enclosing != SD_BUS_TYPE_ARRAY) {
- if (c->signature && c->signature[c->index] != 0)
- return -EBUSY;
- }
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- if (m->rindex < c->end)
- return -EBUSY;
-
- } else if (c->enclosing == SD_BUS_TYPE_ARRAY) {
- uint32_t l;
-
- l = BUS_MESSAGE_BSWAP32(m, *c->array_size);
- if (c->begin + l != m->rindex)
- return -EBUSY;
- }
-
- free(c->signature);
- free(c->peeked_signature);
- free(c->offsets);
- m->n_containers--;
-
- c = message_get_container(m);
-
- saved = c->index;
- c->index = c->saved_index;
- r = container_next_item(m, c, &m->rindex);
- c->index = saved;
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static void message_quit_container(sd_bus_message *m) {
- struct bus_container *c;
-
- assert(m);
- assert(m->sealed);
- assert(m->n_containers > 0);
-
- c = message_get_container(m);
-
- /* Undo seeks */
- assert(m->rindex >= c->before);
- m->rindex = c->before;
-
- /* Free container */
- free(c->signature);
- free(c->offsets);
- m->n_containers--;
-
- /* Correct index of new top-level container */
- c = message_get_container(m);
- c->index = c->saved_index;
-}
-
-_public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char **contents) {
- struct bus_container *c;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(m->sealed, -EPERM);
-
- if (message_end_of_signature(m))
- goto eof;
-
- if (message_end_of_array(m, m->rindex))
- goto eof;
-
- c = message_get_container(m);
-
- if (bus_type_is_basic(c->signature[c->index])) {
- if (contents)
- *contents = NULL;
- if (type)
- *type = c->signature[c->index];
- return 1;
- }
-
- if (c->signature[c->index] == SD_BUS_TYPE_ARRAY) {
-
- if (contents) {
- size_t l;
- char *sig;
-
- r = signature_element_length(c->signature+c->index+1, &l);
- if (r < 0)
- return r;
-
- assert(l >= 1);
-
- sig = strndup(c->signature + c->index + 1, l);
- if (!sig)
- return -ENOMEM;
-
- free(c->peeked_signature);
- *contents = c->peeked_signature = sig;
- }
-
- if (type)
- *type = SD_BUS_TYPE_ARRAY;
-
- return 1;
- }
-
- if (c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN ||
- c->signature[c->index] == SD_BUS_TYPE_DICT_ENTRY_BEGIN) {
-
- if (contents) {
- size_t l;
- char *sig;
-
- r = signature_element_length(c->signature+c->index, &l);
- if (r < 0)
- return r;
-
- assert(l >= 2);
- sig = strndup(c->signature + c->index + 1, l - 2);
- if (!sig)
- return -ENOMEM;
-
- free(c->peeked_signature);
- *contents = c->peeked_signature = sig;
- }
-
- if (type)
- *type = c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY;
-
- return 1;
- }
-
- if (c->signature[c->index] == SD_BUS_TYPE_VARIANT) {
- if (contents) {
- void *q;
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- size_t k;
-
- if (c->item_size < 2)
- return -EBADMSG;
-
- /* Look for the NUL delimiter that
- separates the payload from the
- signature. Since the body might be
- in a different part that then the
- signature we map byte by byte. */
-
- for (k = 2; k <= c->item_size; k++) {
- size_t where;
-
- where = m->rindex + c->item_size - k;
- r = message_peek_body(m, &where, 1, k, &q);
- if (r < 0)
- return r;
-
- if (*(char*) q == 0)
- break;
- }
-
- if (k > c->item_size)
- return -EBADMSG;
-
- free(c->peeked_signature);
- c->peeked_signature = strndup((char*) q + 1, k - 1);
- if (!c->peeked_signature)
- return -ENOMEM;
-
- if (!signature_is_valid(c->peeked_signature, true))
- return -EBADMSG;
-
- *contents = c->peeked_signature;
- } else {
- size_t rindex, l;
-
- rindex = m->rindex;
- r = message_peek_body(m, &rindex, 1, 1, &q);
- if (r < 0)
- return r;
-
- l = *(uint8_t*) q;
- r = message_peek_body(m, &rindex, 1, l+1, &q);
- if (r < 0)
- return r;
-
- if (!validate_signature(q, l))
- return -EBADMSG;
-
- *contents = q;
- }
- }
-
- if (type)
- *type = SD_BUS_TYPE_VARIANT;
-
- return 1;
- }
-
- return -EINVAL;
-
-eof:
- if (type)
- *type = 0;
- if (contents)
- *contents = NULL;
- return 0;
-}
-
-_public_ int sd_bus_message_rewind(sd_bus_message *m, int complete) {
- struct bus_container *c;
-
- assert_return(m, -EINVAL);
- assert_return(m->sealed, -EPERM);
-
- if (complete) {
- message_reset_containers(m);
- m->rindex = 0;
-
- c = message_get_container(m);
- } else {
- c = message_get_container(m);
-
- c->offset_index = 0;
- c->index = 0;
- m->rindex = c->begin;
- }
-
- c->offset_index = 0;
- c->item_size = (c->n_offsets > 0 ? c->offsets[0] : c->end) - c->begin;
-
- return !isempty(c->signature);
-}
-
-static int message_read_ap(
- sd_bus_message *m,
- const char *types,
- va_list ap) {
-
- unsigned n_array, n_struct;
- TypeStack stack[BUS_CONTAINER_DEPTH];
- unsigned stack_ptr = 0;
- unsigned n_loop = 0;
- int r;
-
- assert(m);
-
- if (isempty(types))
- return 0;
-
- /* Ideally, we'd just call ourselves recursively on every
- * complex type. However, the state of a va_list that is
- * passed to a function is undefined after that function
- * returns. This means we need to docode the va_list linearly
- * in a single stackframe. We hence implement our own
- * home-grown stack in an array. */
-
- n_array = (unsigned) -1; /* lenght of current array entries */
- n_struct = strlen(types); /* length of current struct contents signature */
-
- for (;;) {
- const char *t;
-
- n_loop++;
-
- if (n_array == 0 || (n_array == (unsigned) -1 && n_struct == 0)) {
- r = type_stack_pop(stack, ELEMENTSOF(stack), &stack_ptr, &types, &n_struct, &n_array);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- continue;
- }
-
- t = types;
- if (n_array != (unsigned) -1)
- n_array --;
- else {
- types ++;
- n_struct--;
- }
-
- switch (*t) {
-
- case SD_BUS_TYPE_BYTE:
- case SD_BUS_TYPE_BOOLEAN:
- case SD_BUS_TYPE_INT16:
- case SD_BUS_TYPE_UINT16:
- case SD_BUS_TYPE_INT32:
- case SD_BUS_TYPE_UINT32:
- case SD_BUS_TYPE_INT64:
- case SD_BUS_TYPE_UINT64:
- case SD_BUS_TYPE_DOUBLE:
- case SD_BUS_TYPE_STRING:
- case SD_BUS_TYPE_OBJECT_PATH:
- case SD_BUS_TYPE_SIGNATURE:
- case SD_BUS_TYPE_UNIX_FD: {
- void *p;
-
- p = va_arg(ap, void*);
- r = sd_bus_message_read_basic(m, *t, p);
- if (r < 0)
- return r;
- if (r == 0) {
- if (n_loop <= 1)
- return 0;
-
- return -ENXIO;
- }
-
- break;
- }
-
- case SD_BUS_TYPE_ARRAY: {
- size_t k;
-
- r = signature_element_length(t + 1, &k);
- if (r < 0)
- return r;
-
- {
- char s[k + 1];
- memcpy(s, t + 1, k);
- s[k] = 0;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, s);
- if (r < 0)
- return r;
- if (r == 0) {
- if (n_loop <= 1)
- return 0;
-
- return -ENXIO;
- }
- }
-
- if (n_array == (unsigned) -1) {
- types += k;
- n_struct -= k;
- }
-
- r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
- if (r < 0)
- return r;
-
- types = t + 1;
- n_struct = k;
- n_array = va_arg(ap, unsigned);
-
- break;
- }
-
- case SD_BUS_TYPE_VARIANT: {
- const char *s;
-
- s = va_arg(ap, const char *);
- if (!s)
- return -EINVAL;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, s);
- if (r < 0)
- return r;
- if (r == 0) {
- if (n_loop <= 1)
- return 0;
-
- return -ENXIO;
- }
-
- r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
- if (r < 0)
- return r;
-
- types = s;
- n_struct = strlen(s);
- n_array = (unsigned) -1;
-
- break;
- }
-
- case SD_BUS_TYPE_STRUCT_BEGIN:
- case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
- size_t k;
-
- r = signature_element_length(t, &k);
- if (r < 0)
- return r;
-
- {
- char s[k - 1];
- memcpy(s, t + 1, k - 2);
- s[k - 2] = 0;
-
- r = sd_bus_message_enter_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
- if (r < 0)
- return r;
- if (r == 0) {
- if (n_loop <= 1)
- return 0;
- return -ENXIO;
- }
- }
-
- if (n_array == (unsigned) -1) {
- types += k - 1;
- n_struct -= k - 1;
- }
-
- r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
- if (r < 0)
- return r;
-
- types = t + 1;
- n_struct = k - 2;
- n_array = (unsigned) -1;
-
- break;
- }
-
- default:
- return -EINVAL;
- }
- }
-
- return 1;
-}
-
-_public_ int sd_bus_message_read(sd_bus_message *m, const char *types, ...) {
- va_list ap;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(m->sealed, -EPERM);
- assert_return(types, -EINVAL);
-
- va_start(ap, types);
- r = message_read_ap(m, types, ap);
- va_end(ap);
-
- return r;
-}
-
-_public_ int sd_bus_message_skip(sd_bus_message *m, const char *types) {
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(m->sealed, -EPERM);
- assert_return(types, -EINVAL);
-
- if (isempty(types))
- return 0;
-
- switch (*types) {
-
- case SD_BUS_TYPE_BYTE:
- case SD_BUS_TYPE_BOOLEAN:
- case SD_BUS_TYPE_INT16:
- case SD_BUS_TYPE_UINT16:
- case SD_BUS_TYPE_INT32:
- case SD_BUS_TYPE_UINT32:
- case SD_BUS_TYPE_INT64:
- case SD_BUS_TYPE_UINT64:
- case SD_BUS_TYPE_DOUBLE:
- case SD_BUS_TYPE_STRING:
- case SD_BUS_TYPE_OBJECT_PATH:
- case SD_BUS_TYPE_SIGNATURE:
- case SD_BUS_TYPE_UNIX_FD:
-
- r = sd_bus_message_read_basic(m, *types, NULL);
- if (r <= 0)
- return r;
-
- r = sd_bus_message_skip(m, types + 1);
- if (r < 0)
- return r;
-
- return 1;
-
- case SD_BUS_TYPE_ARRAY: {
- size_t k;
-
- r = signature_element_length(types + 1, &k);
- if (r < 0)
- return r;
-
- {
- char s[k+1];
- memcpy(s, types+1, k);
- s[k] = 0;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, s);
- if (r <= 0)
- return r;
-
- for (;;) {
- r = sd_bus_message_skip(m, s);
- if (r < 0)
- return r;
- if (r == 0)
- break;
- }
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_skip(m, types + 1 + k);
- if (r < 0)
- return r;
-
- return 1;
- }
-
- case SD_BUS_TYPE_VARIANT: {
- const char *contents;
- char x;
-
- r = sd_bus_message_peek_type(m, &x, &contents);
- if (r <= 0)
- return r;
-
- if (x != SD_BUS_TYPE_VARIANT)
- return -ENXIO;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
- if (r <= 0)
- return r;
-
- r = sd_bus_message_skip(m, contents);
- if (r < 0)
- return r;
- assert(r != 0);
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- r = sd_bus_message_skip(m, types + 1);
- if (r < 0)
- return r;
-
- return 1;
- }
-
- case SD_BUS_TYPE_STRUCT_BEGIN:
- case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
- size_t k;
-
- r = signature_element_length(types, &k);
- if (r < 0)
- return r;
-
- {
- char s[k-1];
- memcpy(s, types+1, k-2);
- s[k-2] = 0;
-
- r = sd_bus_message_enter_container(m, *types == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
- if (r <= 0)
- return r;
-
- r = sd_bus_message_skip(m, s);
- if (r < 0)
- return r;
- assert(r != 0);
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_skip(m, types + k);
- if (r < 0)
- return r;
-
- return 1;
- }
-
- default:
- return -EINVAL;
- }
-}
-
-_public_ int sd_bus_message_read_array(sd_bus_message *m,
- char type,
- const void **ptr,
- size_t *size) {
- struct bus_container *c;
- void *p;
- size_t sz;
- ssize_t align;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(m->sealed, -EPERM);
- assert_return(bus_type_is_trivial(type), -EINVAL);
- assert_return(ptr, -EINVAL);
- assert_return(size, -EINVAL);
- assert_return(!BUS_MESSAGE_NEED_BSWAP(m), -ENOTSUP);
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type));
- if (r <= 0)
- return r;
-
- c = message_get_container(m);
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- align = bus_gvariant_get_alignment(CHAR_TO_STR(type));
- if (align < 0)
- return align;
-
- sz = c->end - c->begin;
- } else {
- align = bus_type_get_alignment(type);
- if (align < 0)
- return align;
-
- sz = BUS_MESSAGE_BSWAP32(m, *c->array_size);
- }
-
- if (sz == 0)
- /* Zero length array, let's return some aligned
- * pointer that is not NULL */
- p = (uint8_t*) NULL + align;
- else {
- r = message_peek_body(m, &m->rindex, align, sz, &p);
- if (r < 0)
- goto fail;
- }
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- goto fail;
-
- *ptr = (const void*) p;
- *size = sz;
-
- return 1;
-
-fail:
- message_quit_container(m);
- return r;
-}
-
-static int message_peek_fields(
- sd_bus_message *m,
- size_t *rindex,
- size_t align,
- size_t nbytes,
- void **ret) {
-
- assert(m);
- assert(rindex);
- assert(align > 0);
-
- return buffer_peek(BUS_MESSAGE_FIELDS(m), BUS_MESSAGE_FIELDS_SIZE(m), rindex, align, nbytes, ret);
-}
-
-static int message_peek_field_uint32(
- sd_bus_message *m,
- size_t *ri,
- size_t item_size,
- uint32_t *ret) {
-
- int r;
- void *q;
-
- assert(m);
- assert(ri);
-
- if (BUS_MESSAGE_IS_GVARIANT(m) && item_size != 4)
- return -EBADMSG;
-
- /* identical for gvariant and dbus1 */
-
- r = message_peek_fields(m, ri, 4, 4, &q);
- if (r < 0)
- return r;
-
- if (ret)
- *ret = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
-
- return 0;
-}
-
-static int message_peek_field_string(
- sd_bus_message *m,
- bool (*validate)(const char *p),
- size_t *ri,
- size_t item_size,
- const char **ret) {
-
- uint32_t l;
- int r;
- void *q;
-
- assert(m);
- assert(ri);
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
-
- if (item_size <= 0)
- return -EBADMSG;
-
- r = message_peek_fields(m, ri, 1, item_size, &q);
- if (r < 0)
- return r;
-
- l = item_size - 1;
- } else {
- r = message_peek_field_uint32(m, ri, 4, &l);
- if (r < 0)
- return r;
-
- r = message_peek_fields(m, ri, 1, l+1, &q);
- if (r < 0)
- return r;
- }
-
- if (validate) {
- if (!validate_nul(q, l))
- return -EBADMSG;
-
- if (!validate(q))
- return -EBADMSG;
- } else {
- if (!validate_string(q, l))
- return -EBADMSG;
- }
-
- if (ret)
- *ret = q;
-
- return 0;
-}
-
-static int message_peek_field_signature(
- sd_bus_message *m,
- size_t *ri,
- size_t item_size,
- const char **ret) {
-
- size_t l;
- int r;
- void *q;
-
- assert(m);
- assert(ri);
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
-
- if (item_size <= 0)
- return -EBADMSG;
-
- r = message_peek_fields(m, ri, 1, item_size, &q);
- if (r < 0)
- return r;
-
- l = item_size - 1;
- } else {
- r = message_peek_fields(m, ri, 1, 1, &q);
- if (r < 0)
- return r;
-
- l = *(uint8_t*) q;
- r = message_peek_fields(m, ri, 1, l+1, &q);
- if (r < 0)
- return r;
- }
-
- if (!validate_signature(q, l))
- return -EBADMSG;
-
- if (ret)
- *ret = q;
-
- return 0;
-}
-
-static int message_skip_fields(
- sd_bus_message *m,
- size_t *ri,
- uint32_t array_size,
- const char **signature) {
-
- size_t original_index;
- int r;
-
- assert(m);
- assert(ri);
- assert(signature);
- assert(!BUS_MESSAGE_IS_GVARIANT(m));
-
- original_index = *ri;
-
- for (;;) {
- char t;
- size_t l;
-
- if (array_size != (uint32_t) -1 &&
- array_size <= *ri - original_index)
- return 0;
-
- t = **signature;
- if (!t)
- return 0;
-
- if (t == SD_BUS_TYPE_STRING) {
-
- r = message_peek_field_string(m, NULL, ri, 0, NULL);
- if (r < 0)
- return r;
-
- (*signature)++;
-
- } else if (t == SD_BUS_TYPE_OBJECT_PATH) {
-
- r = message_peek_field_string(m, object_path_is_valid, ri, 0, NULL);
- if (r < 0)
- return r;
-
- (*signature)++;
-
- } else if (t == SD_BUS_TYPE_SIGNATURE) {
-
- r = message_peek_field_signature(m, ri, 0, NULL);
- if (r < 0)
- return r;
-
- (*signature)++;
-
- } else if (bus_type_is_basic(t)) {
- ssize_t align, k;
-
- align = bus_type_get_alignment(t);
- k = bus_type_get_size(t);
- assert(align > 0 && k > 0);
-
- r = message_peek_fields(m, ri, align, k, NULL);
- if (r < 0)
- return r;
-
- (*signature)++;
-
- } else if (t == SD_BUS_TYPE_ARRAY) {
-
- r = signature_element_length(*signature+1, &l);
- if (r < 0)
- return r;
-
- assert(l >= 1);
- {
- char sig[l-1], *s;
- uint32_t nas;
- int alignment;
-
- strncpy(sig, *signature + 1, l-1);
- s = sig;
-
- alignment = bus_type_get_alignment(sig[0]);
- if (alignment < 0)
- return alignment;
-
- r = message_peek_field_uint32(m, ri, 0, &nas);
- if (r < 0)
- return r;
- if (nas > BUS_ARRAY_MAX_SIZE)
- return -EBADMSG;
-
- r = message_peek_fields(m, ri, alignment, 0, NULL);
- if (r < 0)
- return r;
-
- r = message_skip_fields(m, ri, nas, (const char**) &s);
- if (r < 0)
- return r;
- }
-
- (*signature) += 1 + l;
-
- } else if (t == SD_BUS_TYPE_VARIANT) {
- const char *s;
-
- r = message_peek_field_signature(m, ri, 0, &s);
- if (r < 0)
- return r;
-
- r = message_skip_fields(m, ri, (uint32_t) -1, (const char**) &s);
- if (r < 0)
- return r;
-
- (*signature)++;
-
- } else if (t == SD_BUS_TYPE_STRUCT ||
- t == SD_BUS_TYPE_DICT_ENTRY) {
-
- r = signature_element_length(*signature, &l);
- if (r < 0)
- return r;
-
- assert(l >= 2);
- {
- char sig[l-1], *s;
- strncpy(sig, *signature + 1, l-1);
- s = sig;
-
- r = message_skip_fields(m, ri, (uint32_t) -1, (const char**) &s);
- if (r < 0)
- return r;
- }
-
- *signature += l;
- } else
- return -EINVAL;
- }
-}
-
-int bus_message_parse_fields(sd_bus_message *m) {
- size_t ri;
- int r;
- uint32_t unix_fds = 0;
- void *offsets = NULL;
- unsigned n_offsets = 0;
- size_t sz = 0;
- unsigned i = 0;
-
- assert(m);
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- void *q;
-
- sz = determine_word_size(BUS_MESSAGE_FIELDS_SIZE(m), 0);
- if (sz > 0) {
- size_t framing;
-
- ri = BUS_MESSAGE_FIELDS_SIZE(m) - sz;
- r = message_peek_fields(m, &ri, 1, sz, &q);
- if (r < 0)
- return r;
-
- framing = read_word_le(q, sz);
- if (framing >= BUS_MESSAGE_FIELDS_SIZE(m) - sz)
- return -EBADMSG;
- if ((BUS_MESSAGE_FIELDS_SIZE(m) - framing) % sz != 0)
- return -EBADMSG;
-
- ri = framing;
- r = message_peek_fields(m, &ri, 1, BUS_MESSAGE_FIELDS_SIZE(m) - framing, &offsets);
- if (r < 0)
- return r;
-
- n_offsets = (BUS_MESSAGE_FIELDS_SIZE(m) - framing) / sz;
- }
- }
-
- ri = 0;
- while (ri < BUS_MESSAGE_FIELDS_SIZE(m)) {
- _cleanup_free_ char *sig = NULL;
- const char *signature;
- uint8_t *header;
- size_t item_size = (size_t) -1;
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- if (i >= n_offsets)
- break;
-
- if (i == 0)
- ri = 0;
- else
- ri = ALIGN_TO(read_word_le((uint8_t*) offsets + (i-1)*sz, sz), 8);
- }
-
- r = message_peek_fields(m, &ri, 8, 1, (void**) &header);
- if (r < 0)
- return r;
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- size_t where, end;
- char *b;
- void *q;
-
- end = read_word_le((uint8_t*) offsets + i*sz, sz);
-
- if (end < ri)
- return -EBADMSG;
-
- where = ri = ALIGN_TO(ri, 8);
- item_size = end - ri;
- r = message_peek_fields(m, &where, 1, item_size, &q);
- if (r < 0)
- return r;
-
- b = memrchr(q, 0, item_size);
- if (!b)
- return -EBADMSG;
-
- sig = strndup(b+1, item_size - (b+1-(char*) q));
- if (!sig)
- return -ENOMEM;
-
- signature = sig;
- item_size = b - (char*) q;
- } else {
- r = message_peek_field_signature(m, &ri, 0, &signature);
- if (r < 0)
- return r;
- }
-
- switch (*header) {
- case _BUS_MESSAGE_HEADER_INVALID:
- return -EBADMSG;
-
- case BUS_MESSAGE_HEADER_PATH:
-
- if (m->path)
- return -EBADMSG;
-
- if (!streq(signature, "o"))
- return -EBADMSG;
-
- r = message_peek_field_string(m, object_path_is_valid, &ri, item_size, &m->path);
- break;
-
- case BUS_MESSAGE_HEADER_INTERFACE:
-
- if (m->interface)
- return -EBADMSG;
-
- if (!streq(signature, "s"))
- return -EBADMSG;
-
- r = message_peek_field_string(m, interface_name_is_valid, &ri, item_size, &m->interface);
- break;
-
- case BUS_MESSAGE_HEADER_MEMBER:
-
- if (m->member)
- return -EBADMSG;
-
- if (!streq(signature, "s"))
- return -EBADMSG;
-
- r = message_peek_field_string(m, member_name_is_valid, &ri, item_size, &m->member);
- break;
-
- case BUS_MESSAGE_HEADER_ERROR_NAME:
-
- if (m->error.name)
- return -EBADMSG;
-
- if (!streq(signature, "s"))
- return -EBADMSG;
-
- r = message_peek_field_string(m, error_name_is_valid, &ri, item_size, &m->error.name);
- if (r >= 0)
- m->error._need_free = -1;
-
- break;
-
- case BUS_MESSAGE_HEADER_DESTINATION:
-
- if (m->destination)
- return -EBADMSG;
-
- if (!streq(signature, "s"))
- return -EBADMSG;
-
- r = message_peek_field_string(m, service_name_is_valid, &ri, item_size, &m->destination);
- break;
-
- case BUS_MESSAGE_HEADER_SENDER:
-
- if (m->sender)
- return -EBADMSG;
-
- if (!streq(signature, "s"))
- return -EBADMSG;
-
- r = message_peek_field_string(m, service_name_is_valid, &ri, item_size, &m->sender);
-
- if (r >= 0 && m->sender[0] == ':' && m->bus && m->bus->bus_client && !m->bus->is_kernel) {
- m->creds.unique_name = (char*) m->sender;
- m->creds.mask |= SD_BUS_CREDS_UNIQUE_NAME & m->bus->creds_mask;
- }
-
- break;
-
-
- case BUS_MESSAGE_HEADER_SIGNATURE: {
- const char *s;
- char *c;
-
- if (m->root_container.signature)
- return -EBADMSG;
-
- if (!streq(signature, "g"))
- return -EBADMSG;
-
- r = message_peek_field_signature(m, &ri, item_size, &s);
- if (r < 0)
- return r;
-
- c = strdup(s);
- if (!c)
- return -ENOMEM;
-
- free(m->root_container.signature);
- m->root_container.signature = c;
- break;
- }
-
- case BUS_MESSAGE_HEADER_REPLY_SERIAL:
- if (m->reply_cookie != 0)
- return -EBADMSG;
-
- if (!streq(signature, "u"))
- return -EBADMSG;
-
- r = message_peek_field_uint32(m, &ri, item_size, &m->reply_cookie);
- if (r < 0)
- return r;
-
- if (m->reply_cookie == 0)
- return -EBADMSG;
-
- break;
-
- case BUS_MESSAGE_HEADER_UNIX_FDS:
- if (unix_fds != 0)
- return -EBADMSG;
-
- if (!streq(signature, "u"))
- return -EBADMSG;
-
- r = message_peek_field_uint32(m, &ri, item_size, &unix_fds);
- if (r < 0)
- return -EBADMSG;
-
- if (unix_fds == 0)
- return -EBADMSG;
-
- break;
-
- default:
- if (!BUS_MESSAGE_IS_GVARIANT(m))
- r = message_skip_fields(m, &ri, (uint32_t) -1, (const char **) &signature);
- }
-
- if (r < 0)
- return r;
-
- i++;
- }
-
- if (m->n_fds != unix_fds)
- return -EBADMSG;
-
- switch (m->header->type) {
-
- case SD_BUS_MESSAGE_SIGNAL:
- if (!m->path || !m->interface || !m->member)
- return -EBADMSG;
- break;
-
- case SD_BUS_MESSAGE_METHOD_CALL:
-
- if (!m->path || !m->member)
- return -EBADMSG;
-
- break;
-
- case SD_BUS_MESSAGE_METHOD_RETURN:
-
- if (m->reply_cookie == 0)
- return -EBADMSG;
- break;
-
- case SD_BUS_MESSAGE_METHOD_ERROR:
-
- if (m->reply_cookie == 0 || !m->error.name)
- return -EBADMSG;
- break;
- }
-
- /* Refuse non-local messages that claim they are local */
- if (streq_ptr(m->path, "/org/freedesktop/DBus/Local"))
- return -EBADMSG;
- if (streq_ptr(m->interface, "org.freedesktop.DBus.Local"))
- return -EBADMSG;
- if (streq_ptr(m->sender, "org.freedesktop.DBus.Local"))
- return -EBADMSG;
-
- m->root_container.end = BUS_MESSAGE_BODY_SIZE(m);
-
- if (BUS_MESSAGE_IS_GVARIANT(m)) {
- r = build_struct_offsets(
- m,
- m->root_container.signature,
- BUS_MESSAGE_BODY_SIZE(m),
- &m->root_container.item_size,
- &m->root_container.offsets,
- &m->root_container.n_offsets);
- if (r < 0)
- return r;
- }
-
- /* Try to read the error message, but if we can't it's a non-issue */
- if (m->header->type == SD_BUS_MESSAGE_METHOD_ERROR)
- sd_bus_message_read(m, "s", &m->error.message);
-
- return 0;
-}
-
-_public_ int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) {
- assert_return(m, -EINVAL);
- assert_return(destination, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(!m->destination, -EEXIST);
-
- return message_append_field_string(m, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &m->destination);
-}
-
-int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) {
- size_t total;
- void *p, *e;
- unsigned i;
- struct bus_body_part *part;
-
- assert(m);
- assert(buffer);
- assert(sz);
-
- total = BUS_MESSAGE_SIZE(m);
-
- p = malloc(total);
- if (!p)
- return -ENOMEM;
-
- e = mempcpy(p, m->header, BUS_MESSAGE_BODY_BEGIN(m));
- MESSAGE_FOREACH_PART(part, i, m)
- e = mempcpy(e, part->data, part->size);
-
- assert(total == (size_t) ((uint8_t*) e - (uint8_t*) p));
-
- *buffer = p;
- *sz = total;
-
- return 0;
-}
-
-int bus_message_read_strv_extend(sd_bus_message *m, char ***l) {
- int r;
-
- assert(m);
- assert(l);
-
- r = sd_bus_message_enter_container(m, 'a', "s");
- if (r <= 0)
- return r;
-
- for (;;) {
- const char *s;
-
- r = sd_bus_message_read_basic(m, 's', &s);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = strv_extend(l, s);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-_public_ int sd_bus_message_read_strv(sd_bus_message *m, char ***l) {
- char **strv = NULL;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(m->sealed, -EPERM);
- assert_return(l, -EINVAL);
-
- r = bus_message_read_strv_extend(m, &strv);
- if (r <= 0) {
- strv_free(strv);
- return r;
- }
-
- *l = strv;
- return 1;
-}
-
-const char* bus_message_get_arg(sd_bus_message *m, unsigned i) {
- int r;
- const char *t = NULL;
- unsigned j;
-
- assert(m);
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return NULL;
-
- for (j = 0; j <= i; j++) {
- char type;
-
- r = sd_bus_message_peek_type(m, &type, NULL);
- if (r < 0)
- return NULL;
-
- if (type != SD_BUS_TYPE_STRING &&
- type != SD_BUS_TYPE_OBJECT_PATH &&
- type != SD_BUS_TYPE_SIGNATURE)
- return NULL;
-
- r = sd_bus_message_read_basic(m, type, &t);
- if (r < 0)
- return NULL;
- }
-
- return t;
-}
-
-bool bus_header_is_complete(struct bus_header *h, size_t size) {
- size_t full;
-
- assert(h);
- assert(size);
-
- if (size < sizeof(struct bus_header))
- return false;
-
- full = sizeof(struct bus_header) +
- (h->endian == BUS_NATIVE_ENDIAN ? h->fields_size : bswap_32(h->fields_size));
-
- return size >= full;
-}
-
-int bus_header_message_size(struct bus_header *h, size_t *sum) {
- size_t fs, bs;
-
- assert(h);
- assert(sum);
-
- if (h->endian == BUS_NATIVE_ENDIAN) {
- fs = h->fields_size;
- bs = h->body_size;
- } else if (h->endian == BUS_REVERSE_ENDIAN) {
- fs = bswap_32(h->fields_size);
- bs = bswap_32(h->body_size);
- } else
- return -EBADMSG;
-
- *sum = sizeof(struct bus_header) + ALIGN8(fs) + bs;
- return 0;
-}
-
-_public_ int sd_bus_message_get_errno(sd_bus_message *m) {
- assert_return(m, -EINVAL);
-
- if (m->header->type != SD_BUS_MESSAGE_METHOD_ERROR)
- return 0;
-
- return sd_bus_error_get_errno(&m->error);
-}
-
-_public_ const char* sd_bus_message_get_signature(sd_bus_message *m, int complete) {
- struct bus_container *c;
-
- assert_return(m, NULL);
-
- c = complete ? &m->root_container : message_get_container(m);
- return strempty(c->signature);
-}
-
-_public_ int sd_bus_message_copy(sd_bus_message *m, sd_bus_message *source, int all) {
- bool done_something = false;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(source, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(source->sealed, -EPERM);
-
- do {
- const char *contents;
- char type;
- union {
- uint8_t u8;
- uint16_t u16;
- int16_t s16;
- uint32_t u32;
- int32_t s32;
- uint64_t u64;
- int64_t s64;
- double d64;
- const char *string;
- int i;
- } basic;
-
- r = sd_bus_message_peek_type(source, &type, &contents);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- done_something = true;
-
- if (bus_type_is_container(type) > 0) {
-
- r = sd_bus_message_enter_container(source, type, contents);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(m, type, contents);
- if (r < 0)
- return r;
-
- r = sd_bus_message_copy(m, source, true);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(source);
- if (r < 0)
- return r;
-
- continue;
- }
-
- r = sd_bus_message_read_basic(source, type, &basic);
- if (r < 0)
- return r;
-
- assert(r > 0);
-
- if (type == SD_BUS_TYPE_OBJECT_PATH ||
- type == SD_BUS_TYPE_SIGNATURE ||
- type == SD_BUS_TYPE_STRING)
- r = sd_bus_message_append_basic(m, type, basic.string);
- else
- r = sd_bus_message_append_basic(m, type, &basic);
-
- if (r < 0)
- return r;
-
- } while (all);
-
- return done_something;
-}
-
-_public_ int sd_bus_message_verify_type(sd_bus_message *m, char type, const char *contents) {
- const char *c;
- char t;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(m->sealed, -EPERM);
- assert_return(!type || bus_type_is_valid(type), -EINVAL);
- assert_return(!contents || signature_is_valid(contents, true), -EINVAL);
- assert_return(type || contents, -EINVAL);
- assert_return(!contents || !type || bus_type_is_container(type), -EINVAL);
-
- r = sd_bus_message_peek_type(m, &t, &c);
- if (r <= 0)
- return r;
-
- if (type != 0 && type != t)
- return 0;
-
- if (contents && !streq_ptr(contents, c))
- return 0;
-
- return 1;
-}
-
-_public_ sd_bus *sd_bus_message_get_bus(sd_bus_message *m) {
- assert_return(m, NULL);
-
- return m->bus;
-}
-
-int bus_message_remarshal(sd_bus *bus, sd_bus_message **m) {
- _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
- usec_t timeout;
- int r;
-
- assert(bus);
- assert(m);
- assert(*m);
-
- switch ((*m)->header->type) {
-
- case SD_BUS_MESSAGE_SIGNAL:
- r = sd_bus_message_new_signal(bus, (*m)->path, (*m)->interface, (*m)->member, &n);
- if (r < 0)
- return r;
-
- break;
-
- case SD_BUS_MESSAGE_METHOD_CALL:
- r = sd_bus_message_new_method_call(bus, (*m)->destination, (*m)->path, (*m)->interface, (*m)->member, &n);
- if (r < 0)
- return r;
-
- break;
-
- case SD_BUS_MESSAGE_METHOD_RETURN:
- case SD_BUS_MESSAGE_METHOD_ERROR:
-
- n = message_new(bus, (*m)->header->type);
- if (!n)
- return -ENOMEM;
-
- n->reply_cookie = (*m)->reply_cookie;
- r = message_append_field_uint32(n, BUS_MESSAGE_HEADER_REPLY_SERIAL, n->reply_cookie);
- if (r < 0)
- return r;
-
- if ((*m)->header->type == SD_BUS_MESSAGE_METHOD_ERROR && (*m)->error.name) {
- r = message_append_field_string(n, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, (*m)->error.name, &n->error.message);
- if (r < 0)
- return r;
-
- n->error._need_free = -1;
- }
-
- break;
-
- default:
- return -EINVAL;
- }
-
- if ((*m)->destination && !n->destination) {
- r = message_append_field_string(n, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, (*m)->destination, &n->destination);
- if (r < 0)
- return r;
- }
-
- if ((*m)->sender && !n->sender) {
- r = message_append_field_string(n, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, (*m)->sender, &n->sender);
- if (r < 0)
- return r;
- }
-
- n->header->flags |= (*m)->header->flags & (BUS_MESSAGE_NO_REPLY_EXPECTED|BUS_MESSAGE_NO_AUTO_START);
-
- r = sd_bus_message_copy(n, *m, true);
- if (r < 0)
- return r;
-
- timeout = (*m)->timeout;
- if (timeout == 0 && !((*m)->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED))
- timeout = BUS_DEFAULT_TIMEOUT;
-
- r = bus_message_seal(n, BUS_MESSAGE_COOKIE(*m), timeout);
- if (r < 0)
- return r;
-
- sd_bus_message_unref(*m);
- *m = n;
- n = NULL;
-
- return 0;
-}
-
-int bus_message_append_sender(sd_bus_message *m, const char *sender) {
- assert(m);
- assert(sender);
-
- assert_return(!m->sealed, -EPERM);
- assert_return(!m->sender, -EPERM);
-
- return message_append_field_string(m, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, sender, &m->sender);
-}
diff --git a/src/libsystemd/bus-message.h b/src/libsystemd/bus-message.h
deleted file mode 100644
index 5322375..0000000
--- a/src/libsystemd/bus-message.h
+++ /dev/null
@@ -1,244 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-#include <byteswap.h>
-#include <sys/socket.h>
-
-#include "macro.h"
-#include "sd-bus.h"
-#include "kdbus.h"
-#include "time-util.h"
-#include "bus-creds.h"
-#include "bus-protocol.h"
-
-struct bus_container {
- char enclosing;
- bool need_offsets:1;
-
- /* Indexes into the signature string */
- unsigned index, saved_index;
- char *signature;
-
- size_t before, begin, end;
-
- /* dbus1: pointer to the array size value, if this is a value */
- uint32_t *array_size;
-
- /* gvariant: list of offsets to end of children if this is struct/dict entry/array */
- size_t *offsets, n_offsets, offsets_allocated, offset_index;
- size_t item_size;
-
- char *peeked_signature;
-};
-
-struct bus_header {
- uint8_t endian;
- uint8_t type;
- uint8_t flags;
- uint8_t version;
- uint32_t body_size;
-
- /* Note that what the bus spec calls "serial" we'll call
- "cookie" instead, because we don't want to imply that the
- cookie was in any way monotonically increasing. */
- uint32_t serial;
- uint32_t fields_size;
-} _packed_;
-
-struct bus_body_part {
- struct bus_body_part *next;
- void *data;
- size_t size;
- size_t mapped;
- size_t allocated;
- int memfd;
- bool free_this:1;
- bool munmap_this:1;
- bool sealed:1;
- bool is_zero:1;
-};
-
-struct sd_bus_message {
- unsigned n_ref;
-
- sd_bus *bus;
-
- uint32_t reply_cookie;
-
- const char *path;
- const char *interface;
- const char *member;
- const char *destination;
- const char *sender;
-
- sd_bus_error error;
-
- sd_bus_creds creds;
-
- usec_t monotonic;
- usec_t realtime;
-
- bool sealed:1;
- bool dont_send:1;
- bool allow_fds:1;
- bool free_header:1;
- bool free_kdbus:1;
- bool free_fds:1;
- bool release_kdbus:1;
- bool poisoned:1;
-
- struct bus_header *header;
- struct bus_body_part body;
- struct bus_body_part *body_end;
- unsigned n_body_parts;
-
- size_t rindex;
- struct bus_body_part *cached_rindex_part;
- size_t cached_rindex_part_begin;
-
- uint32_t n_fds;
- int *fds;
-
- struct bus_container root_container, *containers;
- unsigned n_containers;
- size_t containers_allocated;
-
- struct iovec *iovec;
- struct iovec iovec_fixed[2];
- unsigned n_iovec;
-
- struct kdbus_msg *kdbus;
-
- char *peeked_signature;
-
- /* If set replies to this message must carry the signature
- * specified here to successfully seal. This is initialized
- * from the vtable data */
- const char *enforced_reply_signature;
-
- usec_t timeout;
-
- char sender_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1];
- char destination_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1];
-
- size_t header_offsets[_BUS_MESSAGE_HEADER_MAX];
- unsigned n_header_offsets;
-};
-
-#define BUS_MESSAGE_NEED_BSWAP(m) ((m)->header->endian != BUS_NATIVE_ENDIAN)
-
-static inline uint16_t BUS_MESSAGE_BSWAP16(sd_bus_message *m, uint16_t u) {
- return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_16(u) : u;
-}
-
-static inline uint32_t BUS_MESSAGE_BSWAP32(sd_bus_message *m, uint32_t u) {
- return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_32(u) : u;
-}
-
-static inline uint64_t BUS_MESSAGE_BSWAP64(sd_bus_message *m, uint64_t u) {
- return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_64(u) : u;
-}
-
-static inline uint32_t BUS_MESSAGE_COOKIE(sd_bus_message *m) {
- return BUS_MESSAGE_BSWAP32(m, m->header->serial);
-}
-
-static inline uint32_t BUS_MESSAGE_BODY_SIZE(sd_bus_message *m) {
- return BUS_MESSAGE_BSWAP32(m, m->header->body_size);
-}
-
-static inline uint32_t BUS_MESSAGE_FIELDS_SIZE(sd_bus_message *m) {
- return BUS_MESSAGE_BSWAP32(m, m->header->fields_size);
-}
-
-static inline uint32_t BUS_MESSAGE_SIZE(sd_bus_message *m) {
- return
- sizeof(struct bus_header) +
- ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) +
- BUS_MESSAGE_BODY_SIZE(m);
-}
-
-static inline uint32_t BUS_MESSAGE_BODY_BEGIN(sd_bus_message *m) {
- return
- sizeof(struct bus_header) +
- ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m));
-}
-
-static inline void* BUS_MESSAGE_FIELDS(sd_bus_message *m) {
- return (uint8_t*) m->header + sizeof(struct bus_header);
-}
-
-static inline bool BUS_MESSAGE_IS_GVARIANT(sd_bus_message *m) {
- return m->header->version == 2;
-}
-
-int bus_message_seal(sd_bus_message *m, uint64_t serial, usec_t timeout);
-int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz);
-int bus_message_read_strv_extend(sd_bus_message *m, char ***l);
-
-int bus_message_from_header(
- sd_bus *bus,
- void *header,
- size_t length,
- int *fds,
- unsigned n_fds,
- const struct ucred *ucred,
- const char *label,
- size_t extra,
- sd_bus_message **ret);
-
-int bus_message_from_malloc(
- sd_bus *bus,
- void *buffer,
- size_t length,
- int *fds,
- unsigned n_fds,
- const struct ucred *ucred,
- const char *label,
- sd_bus_message **ret);
-
-const char* bus_message_get_arg(sd_bus_message *m, unsigned i);
-
-int bus_message_append_ap(sd_bus_message *m, const char *types, va_list ap);
-
-int bus_message_parse_fields(sd_bus_message *m);
-
-bool bus_header_is_complete(struct bus_header *h, size_t size);
-int bus_header_message_size(struct bus_header *h, size_t *sum);
-
-struct bus_body_part *message_append_part(sd_bus_message *m);
-
-#define MESSAGE_FOREACH_PART(part, i, m) \
- for ((i) = 0, (part) = &(m)->body; (i) < (m)->n_body_parts; (i)++, (part) = (part)->next)
-
-int bus_body_part_map(struct bus_body_part *part);
-void bus_body_part_unmap(struct bus_body_part *part);
-
-int bus_message_to_errno(sd_bus_message *m);
-
-int bus_message_new_synthetic_error(sd_bus *bus, uint64_t serial, const sd_bus_error *e, sd_bus_message **m);
-
-int bus_message_remarshal(sd_bus *bus, sd_bus_message **m);
-
-int bus_message_append_sender(sd_bus_message *m, const char *sender);
diff --git a/src/libsystemd/bus-objects.c b/src/libsystemd/bus-objects.c
deleted file mode 100644
index b116a5d..0000000
--- a/src/libsystemd/bus-objects.c
+++ /dev/null
@@ -1,2507 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/capability.h>
-
-#include "strv.h"
-#include "set.h"
-#include "bus-internal.h"
-#include "bus-message.h"
-#include "bus-type.h"
-#include "bus-signature.h"
-#include "bus-introspect.h"
-#include "bus-objects.h"
-#include "bus-util.h"
-
-static int node_vtable_get_userdata(
- sd_bus *bus,
- const char *path,
- struct node_vtable *c,
- void **userdata,
- sd_bus_error *error) {
-
- void *u;
- int r;
-
- assert(bus);
- assert(path);
- assert(c);
-
- u = c->userdata;
- if (c->find) {
- r = c->find(bus, path, c->interface, u, &u, error);
- if (r < 0)
- return r;
- if (sd_bus_error_is_set(error))
- return -sd_bus_error_get_errno(error);
- if (r == 0)
- return r;
- }
-
- if (userdata)
- *userdata = u;
-
- return 1;
-}
-
-static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
- assert(p);
-
- return (uint8_t*) u + p->x.property.offset;
-}
-
-static int vtable_property_get_userdata(
- sd_bus *bus,
- const char *path,
- struct vtable_member *p,
- void **userdata,
- sd_bus_error *error) {
-
- void *u;
- int r;
-
- assert(bus);
- assert(path);
- assert(p);
- assert(userdata);
-
- r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
- if (r <= 0)
- return r;
- if (bus->nodes_modified)
- return 0;
-
- *userdata = vtable_property_convert_userdata(p->vtable, u);
- return 1;
-}
-
-static int add_enumerated_to_set(
- sd_bus *bus,
- const char *prefix,
- struct node_enumerator *first,
- Set *s,
- sd_bus_error *error) {
-
- struct node_enumerator *c;
- int r;
-
- assert(bus);
- assert(prefix);
- assert(s);
-
- LIST_FOREACH(enumerators, c, first) {
- char **children = NULL, **k;
-
- if (bus->nodes_modified)
- return 0;
-
- r = c->callback(bus, prefix, c->userdata, &children, error);
- if (r < 0)
- return r;
- if (sd_bus_error_is_set(error))
- return -sd_bus_error_get_errno(error);
-
- STRV_FOREACH(k, children) {
- if (r < 0) {
- free(*k);
- continue;
- }
-
- if (!object_path_is_valid(*k)){
- free(*k);
- r = -EINVAL;
- continue;
- }
-
- if (!object_path_startswith(*k, prefix)) {
- free(*k);
- continue;
- }
-
- r = set_consume(s, *k);
- if (r == -EEXIST)
- r = 0;
- }
-
- free(children);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int add_subtree_to_set(
- sd_bus *bus,
- const char *prefix,
- struct node *n,
- Set *s,
- sd_bus_error *error) {
-
- struct node *i;
- int r;
-
- assert(bus);
- assert(prefix);
- assert(n);
- assert(s);
-
- r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
-
- LIST_FOREACH(siblings, i, n->child) {
- char *t;
-
- if (!object_path_startswith(i->path, prefix))
- continue;
-
- t = strdup(i->path);
- if (!t)
- return -ENOMEM;
-
- r = set_consume(s, t);
- if (r < 0 && r != -EEXIST)
- return r;
-
- r = add_subtree_to_set(bus, prefix, i, s, error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- }
-
- return 0;
-}
-
-static int get_child_nodes(
- sd_bus *bus,
- const char *prefix,
- struct node *n,
- Set **_s,
- sd_bus_error *error) {
-
- Set *s = NULL;
- int r;
-
- assert(bus);
- assert(prefix);
- assert(n);
- assert(_s);
-
- s = set_new(string_hash_func, string_compare_func);
- if (!s)
- return -ENOMEM;
-
- r = add_subtree_to_set(bus, prefix, n, s, error);
- if (r < 0) {
- set_free_free(s);
- return r;
- }
-
- *_s = s;
- return 0;
-}
-
-static int node_callbacks_run(
- sd_bus *bus,
- sd_bus_message *m,
- struct node_callback *first,
- bool require_fallback,
- bool *found_object) {
-
- struct node_callback *c;
- int r;
-
- assert(bus);
- assert(m);
- assert(found_object);
-
- LIST_FOREACH(callbacks, c, first) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
-
- if (bus->nodes_modified)
- return 0;
-
- if (require_fallback && !c->is_fallback)
- continue;
-
- *found_object = true;
-
- if (c->last_iteration == bus->iteration_counter)
- continue;
-
- c->last_iteration = bus->iteration_counter;
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- r = c->callback(bus, m, c->userdata, &error_buffer);
- r = bus_maybe_reply_error(m, r, &error_buffer);
- if (r != 0)
- return r;
- }
-
- return 0;
-}
-
-#define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
-
-static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
- uint64_t cap;
- uid_t uid;
- int r;
-
- assert(bus);
- assert(m);
- assert(c);
-
- /* If the entire bus is trusted let's grant access */
- if (bus->trusted)
- return 0;
-
- /* If the member is marked UNPRIVILEGED let's grant access */
- if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
- return 0;
-
- /* If we are not connected to kdbus we cannot retrieve the
- * effective capability set without race. Since we need this
- * for a security decision we cannot use racy data, hence
- * don't request it. */
- if (bus->is_kernel)
- r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
- else
- r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
- if (r < 0)
- return r;
-
- /* Check have the caller has the requested capability
- * set. Note that the flags value contains the capability
- * number plus one, which we need to subtract here. We do this
- * so that we have 0 as special value for "default
- * capability". */
- cap = CAPABILITY_SHIFT(c->vtable->flags);
- if (cap == 0)
- cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
- if (cap == 0)
- cap = CAP_SYS_ADMIN;
- else
- cap --;
-
- r = sd_bus_creds_has_effective_cap(creds, cap);
- if (r > 0)
- return 1;
-
- /* Caller has same UID as us, then let's grant access */
- r = sd_bus_creds_get_uid(creds, &uid);
- if (r >= 0) {
- if (uid == getuid())
- return 1;
- }
-
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
-}
-
-static int method_callbacks_run(
- sd_bus *bus,
- sd_bus_message *m,
- struct vtable_member *c,
- bool require_fallback,
- bool *found_object) {
-
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- const char *signature;
- void *u;
- int r;
-
- assert(bus);
- assert(m);
- assert(c);
- assert(found_object);
-
- if (require_fallback && !c->parent->is_fallback)
- return 0;
-
- r = check_access(bus, m, c, &error);
- if (r < 0)
- return bus_maybe_reply_error(m, r, &error);
-
- r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
- if (r <= 0)
- return bus_maybe_reply_error(m, r, &error);
- if (bus->nodes_modified)
- return 0;
-
- *found_object = true;
-
- if (c->last_iteration == bus->iteration_counter)
- return 0;
-
- c->last_iteration = bus->iteration_counter;
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- signature = sd_bus_message_get_signature(m, true);
- if (!signature)
- return -EINVAL;
-
- if (!streq(strempty(c->vtable->x.method.signature), signature))
- return sd_bus_reply_method_errorf(
- m,
- SD_BUS_ERROR_INVALID_ARGS,
- "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
- signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
-
- /* Keep track what the signature of the reply to this message
- * should be, so that this can be enforced when sealing the
- * reply. */
- m->enforced_reply_signature = strempty(c->vtable->x.method.result);
-
- if (c->vtable->x.method.handler) {
- r = c->vtable->x.method.handler(bus, m, u, &error);
- return bus_maybe_reply_error(m, r, &error);
- }
-
- /* If the method callback is NULL, make this a successful NOP */
- r = sd_bus_reply_method_return(m, NULL);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int invoke_property_get(
- sd_bus *bus,
- const sd_bus_vtable *v,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- const void *p;
- int r;
-
- assert(bus);
- assert(v);
- assert(path);
- assert(interface);
- assert(property);
- assert(reply);
-
- if (v->x.property.get) {
- r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
- if (r < 0)
- return r;
- if (sd_bus_error_is_set(error))
- return -sd_bus_error_get_errno(error);
- return r;
- }
-
- /* Automatic handling if no callback is defined. */
-
- if (streq(v->x.property.signature, "as"))
- return sd_bus_message_append_strv(reply, *(char***) userdata);
-
- assert(signature_is_single(v->x.property.signature, false));
- assert(bus_type_is_basic(v->x.property.signature[0]));
-
- switch (v->x.property.signature[0]) {
-
- case SD_BUS_TYPE_STRING:
- case SD_BUS_TYPE_SIGNATURE:
- p = strempty(*(char**) userdata);
- break;
-
- case SD_BUS_TYPE_OBJECT_PATH:
- p = *(char**) userdata;
- assert(p);
- break;
-
- default:
- p = userdata;
- break;
- }
-
- return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
-}
-
-static int invoke_property_set(
- sd_bus *bus,
- const sd_bus_vtable *v,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *value,
- void *userdata,
- sd_bus_error *error) {
-
- int r;
-
- assert(bus);
- assert(v);
- assert(path);
- assert(interface);
- assert(property);
- assert(value);
-
- if (v->x.property.set) {
- r = v->x.property.set(bus, path, interface, property, value, userdata, error);
- if (r < 0)
- return r;
- if (sd_bus_error_is_set(error))
- return -sd_bus_error_get_errno(error);
- return r;
- }
-
- /* Automatic handling if no callback is defined. */
-
- assert(signature_is_single(v->x.property.signature, false));
- assert(bus_type_is_basic(v->x.property.signature[0]));
-
- switch (v->x.property.signature[0]) {
-
- case SD_BUS_TYPE_STRING:
- case SD_BUS_TYPE_OBJECT_PATH:
- case SD_BUS_TYPE_SIGNATURE: {
- const char *p;
- char *n;
-
- r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
- if (r < 0)
- return r;
-
- n = strdup(p);
- if (!n)
- return -ENOMEM;
-
- free(*(char**) userdata);
- *(char**) userdata = n;
-
- break;
- }
-
- default:
- r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
- if (r < 0)
- return r;
-
- break;
- }
-
- return 1;
-}
-
-static int property_get_set_callbacks_run(
- sd_bus *bus,
- sd_bus_message *m,
- struct vtable_member *c,
- bool require_fallback,
- bool is_get,
- bool *found_object) {
-
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- void *u;
- int r;
-
- assert(bus);
- assert(m);
- assert(c);
- assert(found_object);
-
- if (require_fallback && !c->parent->is_fallback)
- return 0;
-
- r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
- if (r <= 0)
- return bus_maybe_reply_error(m, r, &error);
- if (bus->nodes_modified)
- return 0;
-
- *found_object = true;
-
- r = sd_bus_message_new_method_return(m, &reply);
- if (r < 0)
- return r;
-
- if (is_get) {
- /* Note that we do not protect against reexecution
- * here (using the last_iteration check, see below),
- * should the node tree have changed and we got called
- * again. We assume that property Get() calls are
- * ultimately without side-effects or if they aren't
- * then at least idempotent. */
-
- r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
- if (r < 0)
- return r;
-
- /* Note that we do not do an access check here. Read
- * access to properties is always unrestricted, since
- * PropertiesChanged signals broadcast contents
- * anyway. */
-
- r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, u, &error);
- if (r < 0)
- return bus_maybe_reply_error(m, r, &error);
-
- if (bus->nodes_modified)
- return 0;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- } else {
- if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
- return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
-
- /* Avoid that we call the set routine more than once
- * if the processing of this message got restarted
- * because the node tree changed. */
- if (c->last_iteration == bus->iteration_counter)
- return 0;
-
- c->last_iteration = bus->iteration_counter;
-
- r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
- if (r < 0)
- return r;
-
- r = check_access(bus, m, c, &error);
- if (r < 0)
- return bus_maybe_reply_error(m, r, &error);
-
- r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, u, &error);
- if (r < 0)
- return bus_maybe_reply_error(m, r, &error);
-
- if (bus->nodes_modified)
- return 0;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_send(bus, reply, NULL);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int vtable_append_one_property(
- sd_bus *bus,
- sd_bus_message *reply,
- const char *path,
- struct node_vtable *c,
- const sd_bus_vtable *v,
- void *userdata,
- sd_bus_error *error) {
-
- int r;
-
- assert(bus);
- assert(reply);
- assert(path);
- assert(c);
- assert(v);
-
- r = sd_bus_message_open_container(reply, 'e', "sv");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(reply, "s", v->x.property.member);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
- if (r < 0)
- return r;
-
- r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int vtable_append_all_properties(
- sd_bus *bus,
- sd_bus_message *reply,
- const char *path,
- struct node_vtable *c,
- void *userdata,
- sd_bus_error *error) {
-
- const sd_bus_vtable *v;
- int r;
-
- assert(bus);
- assert(reply);
- assert(path);
- assert(c);
-
- if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
- return 1;
-
- for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
- if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
- continue;
-
- if (v->flags & SD_BUS_VTABLE_HIDDEN)
- continue;
-
- r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- }
-
- return 1;
-}
-
-static int property_get_all_callbacks_run(
- sd_bus *bus,
- sd_bus_message *m,
- struct node_vtable *first,
- bool require_fallback,
- const char *iface,
- bool *found_object) {
-
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- struct node_vtable *c;
- bool found_interface;
- int r;
-
- assert(bus);
- assert(m);
- assert(found_object);
-
- r = sd_bus_message_new_method_return(m, &reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'a', "{sv}");
- if (r < 0)
- return r;
-
- found_interface = !iface ||
- streq(iface, "org.freedesktop.DBus.Properties") ||
- streq(iface, "org.freedesktop.DBus.Peer") ||
- streq(iface, "org.freedesktop.DBus.Introspectable");
-
- LIST_FOREACH(vtables, c, first) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- void *u;
-
- if (require_fallback && !c->is_fallback)
- continue;
-
- r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
- if (r < 0)
- return bus_maybe_reply_error(m, r, &error);
- if (bus->nodes_modified)
- return 0;
- if (r == 0)
- continue;
-
- *found_object = true;
-
- if (iface && !streq(c->interface, iface))
- continue;
- found_interface = true;
-
- r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
- if (r < 0)
- return bus_maybe_reply_error(m, r, &error);
- if (bus->nodes_modified)
- return 0;
- }
-
- if (!found_interface) {
- r = sd_bus_reply_method_errorf(
- m,
- SD_BUS_ERROR_UNKNOWN_INTERFACE,
- "Unknown interface '%s'.", iface);
- if (r < 0)
- return r;
-
- return 1;
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- r = sd_bus_send(bus, reply, NULL);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
- assert(bus);
- assert(n);
-
- if (n->object_manager)
- return true;
-
- if (n->parent)
- return bus_node_with_object_manager(bus, n->parent);
-
- return false;
-}
-
-static bool bus_node_exists(
- sd_bus *bus,
- struct node *n,
- const char *path,
- bool require_fallback) {
-
- struct node_vtable *c;
- struct node_callback *k;
-
- assert(bus);
- assert(n);
- assert(path);
-
- /* Tests if there's anything attached directly to this node
- * for the specified path */
-
- LIST_FOREACH(callbacks, k, n->callbacks) {
- if (require_fallback && !k->is_fallback)
- continue;
-
- return true;
- }
-
- LIST_FOREACH(vtables, c, n->vtables) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-
- if (require_fallback && !c->is_fallback)
- continue;
-
- if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
- return true;
- if (bus->nodes_modified)
- return false;
- }
-
- return !require_fallback && (n->enumerators || n->object_manager);
-}
-
-static int process_introspect(
- sd_bus *bus,
- sd_bus_message *m,
- struct node *n,
- bool require_fallback,
- bool *found_object) {
-
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_set_free_free_ Set *s = NULL;
- const char *previous_interface = NULL;
- struct introspect intro;
- struct node_vtable *c;
- bool empty;
- int r;
-
- assert(bus);
- assert(m);
- assert(n);
- assert(found_object);
-
- r = get_child_nodes(bus, m->path, n, &s, &error);
- if (r < 0)
- return bus_maybe_reply_error(m, r, &error);
- if (bus->nodes_modified)
- return 0;
-
- r = introspect_begin(&intro, bus->trusted);
- if (r < 0)
- return r;
-
- r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
- if (r < 0)
- return r;
-
- empty = set_isempty(s);
-
- LIST_FOREACH(vtables, c, n->vtables) {
- if (require_fallback && !c->is_fallback)
- continue;
-
- r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
- if (r < 0) {
- r = bus_maybe_reply_error(m, r, &error);
- goto finish;
- }
- if (bus->nodes_modified) {
- r = 0;
- goto finish;
- }
- if (r == 0)
- continue;
-
- empty = false;
-
- if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
- continue;
-
- if (!streq_ptr(previous_interface, c->interface)) {
-
- if (previous_interface)
- fputs(" </interface>\n", intro.f);
-
- fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
- }
-
- r = introspect_write_interface(&intro, c->vtable);
- if (r < 0)
- goto finish;
-
- previous_interface = c->interface;
- }
-
- if (previous_interface)
- fputs(" </interface>\n", intro.f);
-
- if (empty) {
- /* Nothing?, let's see if we exist at all, and if not
- * refuse to do anything */
- r = bus_node_exists(bus, n, m->path, require_fallback);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- if (r == 0)
- goto finish;
- }
-
- *found_object = true;
-
- r = introspect_write_child_nodes(&intro, s, m->path);
- if (r < 0)
- goto finish;
-
- r = introspect_finish(&intro, bus, m, &reply);
- if (r < 0)
- goto finish;
-
- r = sd_bus_send(bus, reply, NULL);
- if (r < 0)
- goto finish;
-
- r = 1;
-
-finish:
- introspect_free(&intro);
- return r;
-}
-
-static int object_manager_serialize_path(
- sd_bus *bus,
- sd_bus_message *reply,
- const char *prefix,
- const char *path,
- bool require_fallback,
- sd_bus_error *error) {
-
- const char *previous_interface = NULL;
- bool found_something = false;
- struct node_vtable *i;
- struct node *n;
- int r;
-
- assert(bus);
- assert(reply);
- assert(prefix);
- assert(path);
- assert(error);
-
- n = hashmap_get(bus->nodes, prefix);
- if (!n)
- return 0;
-
- LIST_FOREACH(vtables, i, n->vtables) {
- void *u;
-
- if (require_fallback && !i->is_fallback)
- continue;
-
- r = node_vtable_get_userdata(bus, path, i, &u, error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- if (r == 0)
- continue;
-
- if (!found_something) {
-
- /* Open the object part */
-
- r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(reply, "o", path);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
- if (r < 0)
- return r;
-
- found_something = true;
- }
-
- if (!streq_ptr(previous_interface, i->interface)) {
-
- /* Maybe close the previous interface part */
-
- if (previous_interface) {
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
- }
-
- /* Open the new interface part */
-
- r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(reply, "s", i->interface);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'a', "{sv}");
- if (r < 0)
- return r;
- }
-
- r = vtable_append_all_properties(bus, reply, path, i, u, error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
-
- previous_interface = i->interface;
- }
-
- if (previous_interface) {
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
- }
-
- if (found_something) {
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
- }
-
- return 1;
-}
-
-static int object_manager_serialize_path_and_fallbacks(
- sd_bus *bus,
- sd_bus_message *reply,
- const char *path,
- sd_bus_error *error) {
-
- char *prefix;
- int r;
-
- assert(bus);
- assert(reply);
- assert(path);
- assert(error);
-
- /* First, add all vtables registered for this path */
- r = object_manager_serialize_path(bus, reply, path, path, false, error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
-
- /* Second, add fallback vtables registered for any of the prefixes */
- prefix = alloca(strlen(path) + 1);
- OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
- r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- }
-
- return 0;
-}
-
-static int process_get_managed_objects(
- sd_bus *bus,
- sd_bus_message *m,
- struct node *n,
- bool require_fallback,
- bool *found_object) {
-
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_set_free_free_ Set *s = NULL;
- bool empty;
- int r;
-
- assert(bus);
- assert(m);
- assert(n);
- assert(found_object);
-
- if (!bus_node_with_object_manager(bus, n))
- return 0;
-
- r = get_child_nodes(bus, m->path, n, &s, &error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
-
- r = sd_bus_message_new_method_return(m, &reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
- if (r < 0)
- return r;
-
- empty = set_isempty(s);
- if (empty) {
- struct node_vtable *c;
-
- /* Hmm, so we have no children? Then let's check
- * whether we exist at all, i.e. whether at least one
- * vtable exists. */
-
- LIST_FOREACH(vtables, c, n->vtables) {
-
- if (require_fallback && !c->is_fallback)
- continue;
-
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- empty = false;
- break;
- }
-
- if (empty)
- return 0;
- } else {
- Iterator i;
- char *path;
-
- SET_FOREACH(path, s, i) {
- r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
- if (r < 0)
- return r;
-
- if (bus->nodes_modified)
- return 0;
- }
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- r = sd_bus_send(bus, reply, NULL);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int object_find_and_run(
- sd_bus *bus,
- sd_bus_message *m,
- const char *p,
- bool require_fallback,
- bool *found_object) {
-
- struct node *n;
- struct vtable_member vtable_key, *v;
- int r;
-
- assert(bus);
- assert(m);
- assert(p);
- assert(found_object);
-
- n = hashmap_get(bus->nodes, p);
- if (!n)
- return 0;
-
- /* First, try object callbacks */
- r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
- if (r != 0)
- return r;
- if (bus->nodes_modified)
- return 0;
-
- if (!m->interface || !m->member)
- return 0;
-
- /* Then, look for a known method */
- vtable_key.path = (char*) p;
- vtable_key.interface = m->interface;
- vtable_key.member = m->member;
-
- v = hashmap_get(bus->vtable_methods, &vtable_key);
- if (v) {
- r = method_callbacks_run(bus, m, v, require_fallback, found_object);
- if (r != 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- }
-
- /* Then, look for a known property */
- if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
- bool get = false;
-
- get = streq(m->member, "Get");
-
- if (get || streq(m->member, "Set")) {
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- vtable_key.path = (char*) p;
-
- r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
- if (r < 0)
- return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
-
- v = hashmap_get(bus->vtable_properties, &vtable_key);
- if (v) {
- r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
- if (r != 0)
- return r;
- }
-
- } else if (streq(m->member, "GetAll")) {
- const char *iface;
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(m, "s", &iface);
- if (r < 0)
- return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
-
- if (iface[0] == 0)
- iface = NULL;
-
- r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
- if (r != 0)
- return r;
- }
-
- } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
-
- if (!isempty(sd_bus_message_get_signature(m, true)))
- return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
-
- r = process_introspect(bus, m, n, require_fallback, found_object);
- if (r != 0)
- return r;
-
- } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
-
- if (!isempty(sd_bus_message_get_signature(m, true)))
- return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
-
- r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
- if (r != 0)
- return r;
- }
-
- if (bus->nodes_modified)
- return 0;
-
- if (!*found_object) {
- r = bus_node_exists(bus, n, m->path, require_fallback);
- if (r < 0)
- return r;
- if (r > 0)
- *found_object = true;
- }
-
- return 0;
-}
-
-int bus_process_object(sd_bus *bus, sd_bus_message *m) {
- int r;
- size_t pl;
- bool found_object = false;
-
- assert(bus);
- assert(m);
-
- if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
- return 0;
-
- if (hashmap_isempty(bus->nodes))
- return 0;
-
- /* Never respond to broadcast messages */
- if (bus->bus_client && !m->destination)
- return 0;
-
- assert(m->path);
- assert(m->member);
-
- pl = strlen(m->path);
- do {
- char prefix[pl+1];
-
- bus->nodes_modified = false;
-
- r = object_find_and_run(bus, m, m->path, false, &found_object);
- if (r != 0)
- return r;
-
- /* Look for fallback prefixes */
- OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
-
- if (bus->nodes_modified)
- break;
-
- r = object_find_and_run(bus, m, prefix, true, &found_object);
- if (r != 0)
- return r;
- }
-
- } while (bus->nodes_modified);
-
- if (!found_object)
- return 0;
-
- if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
- sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
- r = sd_bus_reply_method_errorf(
- m,
- SD_BUS_ERROR_UNKNOWN_PROPERTY,
- "Unknown property or interface.");
- else
- r = sd_bus_reply_method_errorf(
- m,
- SD_BUS_ERROR_UNKNOWN_METHOD,
- "Unknown method '%s' or interface '%s'.", m->member, m->interface);
-
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
- struct node *n, *parent;
- const char *e;
- _cleanup_free_ char *s = NULL;
- char *p;
- int r;
-
- assert(bus);
- assert(path);
- assert(path[0] == '/');
-
- n = hashmap_get(bus->nodes, path);
- if (n)
- return n;
-
- r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
- if (r < 0)
- return NULL;
-
- s = strdup(path);
- if (!s)
- return NULL;
-
- if (streq(path, "/"))
- parent = NULL;
- else {
- e = strrchr(path, '/');
- assert(e);
-
- p = strndupa(path, MAX(1, path - e));
-
- parent = bus_node_allocate(bus, p);
- if (!parent)
- return NULL;
- }
-
- n = new0(struct node, 1);
- if (!n)
- return NULL;
-
- n->parent = parent;
- n->path = s;
- s = NULL; /* do not free */
-
- r = hashmap_put(bus->nodes, n->path, n);
- if (r < 0) {
- free(n->path);
- free(n);
- return NULL;
- }
-
- if (parent)
- LIST_PREPEND(siblings, parent->child, n);
-
- return n;
-}
-
-static void bus_node_gc(sd_bus *b, struct node *n) {
- assert(b);
-
- if (!n)
- return;
-
- if (n->child ||
- n->callbacks ||
- n->vtables ||
- n->enumerators ||
- n->object_manager)
- return;
-
- assert(hashmap_remove(b->nodes, n->path) == n);
-
- if (n->parent)
- LIST_REMOVE(siblings, n->parent->child, n);
-
- free(n->path);
- bus_node_gc(b, n->parent);
- free(n);
-}
-
-static int bus_add_object(
- sd_bus *bus,
- bool fallback,
- const char *path,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- struct node_callback *c;
- struct node *n;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- n = bus_node_allocate(bus, path);
- if (!n)
- return -ENOMEM;
-
- c = new0(struct node_callback, 1);
- if (!c) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->node = n;
- c->callback = callback;
- c->userdata = userdata;
- c->is_fallback = fallback;
-
- LIST_PREPEND(callbacks, n->callbacks, c);
- bus->nodes_modified = true;
-
- return 0;
-
-fail:
- free(c);
- bus_node_gc(bus, n);
- return r;
-}
-
-static int bus_remove_object(
- sd_bus *bus,
- bool fallback,
- const char *path,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- struct node_callback *c;
- struct node *n;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- n = hashmap_get(bus->nodes, path);
- if (!n)
- return 0;
-
- LIST_FOREACH(callbacks, c, n->callbacks)
- if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
- break;
- if (!c)
- return 0;
-
- LIST_REMOVE(callbacks, n->callbacks, c);
- free(c);
-
- bus_node_gc(bus, n);
- bus->nodes_modified = true;
-
- return 1;
-}
-
-_public_ int sd_bus_add_object(sd_bus *bus,
- const char *path,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- return bus_add_object(bus, false, path, callback, userdata);
-}
-
-_public_ int sd_bus_remove_object(sd_bus *bus,
- const char *path,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- return bus_remove_object(bus, false, path, callback, userdata);
-}
-
-_public_ int sd_bus_add_fallback(sd_bus *bus,
- const char *prefix,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- return bus_add_object(bus, true, prefix, callback, userdata);
-}
-
-_public_ int sd_bus_remove_fallback(sd_bus *bus,
- const char *prefix,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- return bus_remove_object(bus, true, prefix, callback, userdata);
-}
-
-static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
- assert(bus);
-
- if (!w)
- return;
-
- if (w->interface && w->node && w->vtable) {
- const sd_bus_vtable *v;
-
- for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
- struct vtable_member *x = NULL;
-
- switch (v->type) {
-
- case _SD_BUS_VTABLE_METHOD: {
- struct vtable_member key;
-
- key.path = w->node->path;
- key.interface = w->interface;
- key.member = v->x.method.member;
-
- x = hashmap_remove(bus->vtable_methods, &key);
- break;
- }
-
- case _SD_BUS_VTABLE_PROPERTY:
- case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
- struct vtable_member key;
-
- key.path = w->node->path;
- key.interface = w->interface;
- key.member = v->x.property.member;
- x = hashmap_remove(bus->vtable_properties, &key);
- break;
- }}
-
- free(x);
- }
- }
-
- free(w->interface);
- free(w);
-}
-
-static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
- const struct vtable_member *m = a;
- uint8_t hash_key2[HASH_KEY_SIZE];
- unsigned long ret;
-
- assert(m);
-
- ret = string_hash_func(m->path, hash_key);
-
- /* Use a slightly different hash key for the interface */
- memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
- hash_key2[0]++;
- ret ^= string_hash_func(m->interface, hash_key2);
-
- /* And an even different one for the member */
- hash_key2[0]++;
- ret ^= string_hash_func(m->member, hash_key2);
-
- return ret;
-}
-
-static int vtable_member_compare_func(const void *a, const void *b) {
- const struct vtable_member *x = a, *y = b;
- int r;
-
- assert(x);
- assert(y);
-
- r = strcmp(x->path, y->path);
- if (r != 0)
- return r;
-
- r = strcmp(x->interface, y->interface);
- if (r != 0)
- return r;
-
- return strcmp(x->member, y->member);
-}
-
-static int add_object_vtable_internal(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const sd_bus_vtable *vtable,
- bool fallback,
- sd_bus_object_find_t find,
- void *userdata) {
-
- struct node_vtable *c = NULL, *i, *existing = NULL;
- const sd_bus_vtable *v;
- struct node *n;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(interface_name_is_valid(interface), -EINVAL);
- assert_return(vtable, -EINVAL);
- assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
- assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
- !streq(interface, "org.freedesktop.DBus.Introspectable") &&
- !streq(interface, "org.freedesktop.DBus.Peer") &&
- !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
-
- r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
- if (r < 0)
- return r;
-
- r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
- if (r < 0)
- return r;
-
- n = bus_node_allocate(bus, path);
- if (!n)
- return -ENOMEM;
-
- LIST_FOREACH(vtables, i, n->vtables) {
- if (i->is_fallback != fallback) {
- r = -EPROTOTYPE;
- goto fail;
- }
-
- if (streq(i->interface, interface)) {
-
- if (i->vtable == vtable) {
- r = -EEXIST;
- goto fail;
- }
-
- existing = i;
- }
- }
-
- c = new0(struct node_vtable, 1);
- if (!c) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->node = n;
- c->is_fallback = fallback;
- c->vtable = vtable;
- c->userdata = userdata;
- c->find = find;
-
- c->interface = strdup(interface);
- if (!c->interface) {
- r = -ENOMEM;
- goto fail;
- }
-
- for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
-
- switch (v->type) {
-
- case _SD_BUS_VTABLE_METHOD: {
- struct vtable_member *m;
-
- if (!member_name_is_valid(v->x.method.member) ||
- !signature_is_valid(strempty(v->x.method.signature), false) ||
- !signature_is_valid(strempty(v->x.method.result), false) ||
- !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
- v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
- r = -EINVAL;
- goto fail;
- }
-
- m = new0(struct vtable_member, 1);
- if (!m) {
- r = -ENOMEM;
- goto fail;
- }
-
- m->parent = c;
- m->path = n->path;
- m->interface = c->interface;
- m->member = v->x.method.member;
- m->vtable = v;
-
- r = hashmap_put(bus->vtable_methods, m, m);
- if (r < 0) {
- free(m);
- goto fail;
- }
-
- break;
- }
-
- case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
-
- if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
- r = -EINVAL;
- goto fail;
- }
-
- /* Fall through */
-
- case _SD_BUS_VTABLE_PROPERTY: {
- struct vtable_member *m;
-
- if (!member_name_is_valid(v->x.property.member) ||
- !signature_is_single(v->x.property.signature, false) ||
- !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
- v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
- (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
- (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
- r = -EINVAL;
- goto fail;
- }
-
- m = new0(struct vtable_member, 1);
- if (!m) {
- r = -ENOMEM;
- goto fail;
- }
-
- m->parent = c;
- m->path = n->path;
- m->interface = c->interface;
- m->member = v->x.property.member;
- m->vtable = v;
-
- r = hashmap_put(bus->vtable_properties, m, m);
- if (r < 0) {
- free(m);
- goto fail;
- }
-
- break;
- }
-
- case _SD_BUS_VTABLE_SIGNAL:
-
- if (!member_name_is_valid(v->x.signal.member) ||
- !signature_is_valid(strempty(v->x.signal.signature), false) ||
- v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
- r = -EINVAL;
- goto fail;
- }
-
- break;
-
- default:
- r = -EINVAL;
- goto fail;
- }
- }
-
- LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
- bus->nodes_modified = true;
-
- return 0;
-
-fail:
- if (c)
- free_node_vtable(bus, c);
-
- bus_node_gc(bus, n);
- return r;
-}
-
-static int remove_object_vtable_internal(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const sd_bus_vtable *vtable,
- bool fallback,
- sd_bus_object_find_t find,
- void *userdata) {
-
- struct node_vtable *c;
- struct node *n;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(interface_name_is_valid(interface), -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- n = hashmap_get(bus->nodes, path);
- if (!n)
- return 0;
-
- LIST_FOREACH(vtables, c, n->vtables)
- if (streq(c->interface, interface) &&
- c->is_fallback == fallback &&
- c->vtable == vtable &&
- c->find == find &&
- c->userdata == userdata)
- break;
-
- if (!c)
- return 0;
-
- LIST_REMOVE(vtables, n->vtables, c);
-
- free_node_vtable(bus, c);
- bus_node_gc(bus, n);
-
- bus->nodes_modified = true;
-
- return 1;
-}
-
-_public_ int sd_bus_add_object_vtable(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const sd_bus_vtable *vtable,
- void *userdata) {
-
- return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
-}
-
-_public_ int sd_bus_remove_object_vtable(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const sd_bus_vtable *vtable,
- void *userdata) {
-
- return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
-}
-
-_public_ int sd_bus_add_fallback_vtable(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const sd_bus_vtable *vtable,
- sd_bus_object_find_t find,
- void *userdata) {
-
- return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
-}
-
-_public_ int sd_bus_remove_fallback_vtable(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const sd_bus_vtable *vtable,
- sd_bus_object_find_t find,
- void *userdata) {
-
- return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
-}
-
-_public_ int sd_bus_add_node_enumerator(
- sd_bus *bus,
- const char *path,
- sd_bus_node_enumerator_t callback,
- void *userdata) {
-
- struct node_enumerator *c;
- struct node *n;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- n = bus_node_allocate(bus, path);
- if (!n)
- return -ENOMEM;
-
- c = new0(struct node_enumerator, 1);
- if (!c) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->node = n;
- c->callback = callback;
- c->userdata = userdata;
-
- LIST_PREPEND(enumerators, n->enumerators, c);
-
- bus->nodes_modified = true;
-
- return 0;
-
-fail:
- free(c);
- bus_node_gc(bus, n);
- return r;
-}
-
-_public_ int sd_bus_remove_node_enumerator(
- sd_bus *bus,
- const char *path,
- sd_bus_node_enumerator_t callback,
- void *userdata) {
-
- struct node_enumerator *c;
- struct node *n;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- n = hashmap_get(bus->nodes, path);
- if (!n)
- return 0;
-
- LIST_FOREACH(enumerators, c, n->enumerators)
- if (c->callback == callback && c->userdata == userdata)
- break;
-
- if (!c)
- return 0;
-
- LIST_REMOVE(enumerators, n->enumerators, c);
- free(c);
-
- bus_node_gc(bus, n);
-
- bus->nodes_modified = true;
-
- return 1;
-}
-
-static int emit_properties_changed_on_interface(
- sd_bus *bus,
- const char *prefix,
- const char *path,
- const char *interface,
- bool require_fallback,
- bool *found_interface,
- char **names) {
-
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- bool has_invalidating = false, has_changing = false;
- struct vtable_member key = {};
- struct node_vtable *c;
- struct node *n;
- char **property;
- void *u = NULL;
- int r;
-
- assert(bus);
- assert(prefix);
- assert(path);
- assert(interface);
- assert(found_interface);
-
- n = hashmap_get(bus->nodes, prefix);
- if (!n)
- return 0;
-
- r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "s", interface);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(m, 'a', "{sv}");
- if (r < 0)
- return r;
-
- key.path = prefix;
- key.interface = interface;
-
- LIST_FOREACH(vtables, c, n->vtables) {
- if (require_fallback && !c->is_fallback)
- continue;
-
- if (!streq(c->interface, interface))
- continue;
-
- r = node_vtable_get_userdata(bus, path, c, &u, &error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- if (r == 0)
- continue;
-
- *found_interface = true;
-
- if (names) {
- /* If the caller specified a list of
- * properties we include exactly those in the
- * PropertiesChanged message */
-
- STRV_FOREACH(property, names) {
- struct vtable_member *v;
-
- assert_return(member_name_is_valid(*property), -EINVAL);
-
- key.member = *property;
- v = hashmap_get(bus->vtable_properties, &key);
- if (!v)
- return -ENOENT;
-
- /* If there are two vtables for the same
- * interface, let's handle this property when
- * we come to that vtable. */
- if (c != v->parent)
- continue;
-
- assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
- v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
-
- assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
-
- if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
- has_invalidating = true;
- continue;
- }
-
- has_changing = true;
-
- r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- }
- } else {
- const sd_bus_vtable *v;
-
- /* If the caller specified no properties list
- * we include all properties that are marked
- * as changing in the message. */
-
- for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
- if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
- continue;
-
- if (v->flags & SD_BUS_VTABLE_HIDDEN)
- continue;
-
- if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
- has_invalidating = true;
- continue;
- }
-
- if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
- continue;
-
- has_changing = true;
-
- r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- }
- }
- }
-
- if (!has_invalidating && !has_changing)
- return 0;
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(m, 'a', "s");
- if (r < 0)
- return r;
-
- if (has_invalidating) {
- LIST_FOREACH(vtables, c, n->vtables) {
- if (require_fallback && !c->is_fallback)
- continue;
-
- if (!streq(c->interface, interface))
- continue;
-
- r = node_vtable_get_userdata(bus, path, c, &u, &error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- if (r == 0)
- continue;
-
- if (names) {
- STRV_FOREACH(property, names) {
- struct vtable_member *v;
-
- key.member = *property;
- assert_se(v = hashmap_get(bus->vtable_properties, &key));
- assert(c == v->parent);
-
- if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
- continue;
-
- r = sd_bus_message_append(m, "s", *property);
- if (r < 0)
- return r;
- }
- } else {
- const sd_bus_vtable *v;
-
- for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
- if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
- continue;
-
- if (v->flags & SD_BUS_VTABLE_HIDDEN)
- continue;
-
- if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
- continue;
-
- r = sd_bus_message_append(m, "s", v->x.property.member);
- if (r < 0)
- return r;
- }
- }
- }
- }
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return r;
-
- r = sd_bus_send(bus, m, NULL);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-_public_ int sd_bus_emit_properties_changed_strv(
- sd_bus *bus,
- const char *path,
- const char *interface,
- char **names) {
-
- BUS_DONT_DESTROY(bus);
- bool found_interface = false;
- char *prefix;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(interface_name_is_valid(interface), -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
-
- /* A non-NULL but empty names list means nothing needs to be
- generated. A NULL list OTOH indicates that all properties
- that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
- included in the PropertiesChanged message. */
- if (names && names[0] == NULL)
- return 0;
-
- do {
- bus->nodes_modified = false;
-
- r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
- if (r != 0)
- return r;
- if (bus->nodes_modified)
- continue;
-
- prefix = alloca(strlen(path) + 1);
- OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
- r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
- if (r != 0)
- return r;
- if (bus->nodes_modified)
- break;
- }
-
- } while (bus->nodes_modified);
-
- return found_interface ? 0 : -ENOENT;
-}
-
-_public_ int sd_bus_emit_properties_changed(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *name, ...) {
-
- char **names;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(interface_name_is_valid(interface), -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (!name)
- return 0;
-
- names = strv_from_stdarg_alloca(name);
-
- return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
-}
-
-static int interfaces_added_append_one_prefix(
- sd_bus *bus,
- sd_bus_message *m,
- const char *prefix,
- const char *path,
- const char *interface,
- bool require_fallback) {
-
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- bool found_interface = false;
- struct node_vtable *c;
- struct node *n;
- void *u = NULL;
- int r;
-
- assert(bus);
- assert(m);
- assert(prefix);
- assert(path);
- assert(interface);
-
- n = hashmap_get(bus->nodes, prefix);
- if (!n)
- return 0;
-
- LIST_FOREACH(vtables, c, n->vtables) {
- if (require_fallback && !c->is_fallback)
- continue;
-
- if (!streq(c->interface, interface))
- continue;
-
- r = node_vtable_get_userdata(bus, path, c, &u, &error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- if (r == 0)
- continue;
-
- if (!found_interface) {
- r = sd_bus_message_append_basic(m, 's', interface);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(m, 'a', "{sv}");
- if (r < 0)
- return r;
-
- found_interface = true;
- }
-
- r = vtable_append_all_properties(bus, m, path, c, u, &error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- }
-
- if (found_interface) {
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return r;
- }
-
- return found_interface;
-}
-
-static int interfaces_added_append_one(
- sd_bus *bus,
- sd_bus_message *m,
- const char *path,
- const char *interface) {
-
- char *prefix;
- int r;
-
- assert(bus);
- assert(m);
- assert(path);
- assert(interface);
-
- r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
- if (r != 0)
- return r;
- if (bus->nodes_modified)
- return 0;
-
- prefix = alloca(strlen(path) + 1);
- OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
- r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
- if (r != 0)
- return r;
- if (bus->nodes_modified)
- return 0;
- }
-
- return -ENOENT;
-}
-
-_public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
- BUS_DONT_DESTROY(bus);
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- char **i;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (strv_isempty(interfaces))
- return 0;
-
- do {
- bus->nodes_modified = false;
-
- if (m)
- m = sd_bus_message_unref(m);
-
- r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append_basic(m, 'o', path);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
- if (r < 0)
- return r;
-
- STRV_FOREACH(i, interfaces) {
- assert_return(interface_name_is_valid(*i), -EINVAL);
-
- r = sd_bus_message_open_container(m, 'e', "sa{sv}");
- if (r < 0)
- return r;
-
- r = interfaces_added_append_one(bus, m, path, *i);
- if (r < 0)
- return r;
-
- if (bus->nodes_modified)
- break;
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return r;
- }
-
- if (bus->nodes_modified)
- continue;
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return r;
-
- } while (bus->nodes_modified);
-
- return sd_bus_send(bus, m, NULL);
-}
-
-_public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
- char **interfaces;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- interfaces = strv_from_stdarg_alloca(interface);
-
- return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
-}
-
-_public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (strv_isempty(interfaces))
- return 0;
-
- r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append_basic(m, 'o', path);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append_strv(m, interfaces);
- if (r < 0)
- return r;
-
- return sd_bus_send(bus, m, NULL);
-}
-
-_public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
- char **interfaces;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- interfaces = strv_from_stdarg_alloca(interface);
-
- return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
-}
-
-_public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
- struct node *n;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- n = bus_node_allocate(bus, path);
- if (!n)
- return -ENOMEM;
-
- n->object_manager = true;
- bus->nodes_modified = true;
- return 0;
-}
-
-_public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
- struct node *n;
-
- assert_return(bus, -EINVAL);
- assert_return(object_path_is_valid(path), -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- n = hashmap_get(bus->nodes, path);
- if (!n)
- return 0;
-
- if (!n->object_manager)
- return 0;
-
- n->object_manager = false;
- bus->nodes_modified = true;
- bus_node_gc(bus, n);
-
- return 1;
-}
diff --git a/src/libsystemd/bus-objects.h b/src/libsystemd/bus-objects.h
deleted file mode 100644
index 420edd9..0000000
--- a/src/libsystemd/bus-objects.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "bus-internal.h"
-
-int bus_process_object(sd_bus *bus, sd_bus_message *m);
diff --git a/src/libsystemd/bus-protocol.h b/src/libsystemd/bus-protocol.h
deleted file mode 100644
index 5046d17..0000000
--- a/src/libsystemd/bus-protocol.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-
-/* Endianness */
-
-enum {
- _BUS_INVALID_ENDIAN = 0,
- BUS_LITTLE_ENDIAN = 'l',
- BUS_BIG_ENDIAN = 'B',
-#if __BYTE_ORDER == __BIG_ENDIAN
- BUS_NATIVE_ENDIAN = BUS_BIG_ENDIAN,
- BUS_REVERSE_ENDIAN = BUS_LITTLE_ENDIAN
-#else
- BUS_NATIVE_ENDIAN = BUS_LITTLE_ENDIAN,
- BUS_REVERSE_ENDIAN = BUS_BIG_ENDIAN
-#endif
-};
-
-/* Flags */
-
-enum {
- BUS_MESSAGE_NO_REPLY_EXPECTED = 1,
- BUS_MESSAGE_NO_AUTO_START = 2
-};
-
-/* Header fields */
-
-enum {
- _BUS_MESSAGE_HEADER_INVALID = 0,
- BUS_MESSAGE_HEADER_PATH,
- BUS_MESSAGE_HEADER_INTERFACE,
- BUS_MESSAGE_HEADER_MEMBER,
- BUS_MESSAGE_HEADER_ERROR_NAME,
- BUS_MESSAGE_HEADER_REPLY_SERIAL,
- BUS_MESSAGE_HEADER_DESTINATION,
- BUS_MESSAGE_HEADER_SENDER,
- BUS_MESSAGE_HEADER_SIGNATURE,
- BUS_MESSAGE_HEADER_UNIX_FDS,
- _BUS_MESSAGE_HEADER_MAX
-};
-
-/* RequestName parameters */
-
-enum {
- BUS_NAME_ALLOW_REPLACEMENT = 1,
- BUS_NAME_REPLACE_EXISTING = 2,
- BUS_NAME_DO_NOT_QUEUE = 4
-};
-
-/* RequestName returns */
-enum {
- BUS_NAME_PRIMARY_OWNER = 1,
- BUS_NAME_IN_QUEUE = 2,
- BUS_NAME_EXISTS = 3,
- BUS_NAME_ALREADY_OWNER = 4
-};
-
-/* ReleaseName returns */
-enum {
- BUS_NAME_RELEASED = 1,
- BUS_NAME_NON_EXISTENT = 2,
- BUS_NAME_NOT_OWNER = 3,
-};
-
-/* StartServiceByName returns */
-enum {
- BUS_START_REPLY_SUCCESS = 1,
- BUS_START_REPLY_ALREADY_RUNNING = 2,
-};
-
-#define BUS_INTROSPECT_DOCTYPE \
- "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \
- "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
-
-#define BUS_INTROSPECT_INTERFACE_PEER \
- " <interface name=\"org.freedesktop.DBus.Peer\">\n" \
- " <method name=\"Ping\"/>\n" \
- " <method name=\"GetMachineId\">\n" \
- " <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>\n" \
- " </method>\n" \
- " </interface>\n"
-
-#define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \
- " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" \
- " <method name=\"Introspect\">\n" \
- " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \
- " </method>\n" \
- " </interface>\n"
-
-#define BUS_INTROSPECT_INTERFACE_PROPERTIES \
- " <interface name=\"org.freedesktop.DBus.Properties\">\n" \
- " <method name=\"Get\">\n" \
- " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
- " <arg name=\"property\" direction=\"in\" type=\"s\"/>\n" \
- " <arg name=\"value\" direction=\"out\" type=\"v\"/>\n" \
- " </method>\n" \
- " <method name=\"GetAll\">\n" \
- " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
- " <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>\n" \
- " </method>\n" \
- " <method name=\"Set\">\n" \
- " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
- " <arg name=\"property\" direction=\"in\" type=\"s\"/>\n" \
- " <arg name=\"value\" direction=\"in\" type=\"v\"/>\n" \
- " </method>\n" \
- " <signal name=\"PropertiesChanged\">\n" \
- " <arg type=\"s\" name=\"interface\"/>\n" \
- " <arg type=\"a{sv}\" name=\"changed_properties\"/>\n" \
- " <arg type=\"as\" name=\"invalidated_properties\"/>\n" \
- " </signal>\n" \
- " </interface>\n"
-
-#define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER \
- " <interface name=\"org.freedesktop.DBus.ObjectManager\">\n" \
- " <method name=\"GetManagedObjects\">\n" \
- " <arg type=\"a{oa{sa{sv}}}\" name=\"object_paths_interfaces_and_properties\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <signal name=\"InterfacesAdded\">\n" \
- " <arg type=\"o\" name=\"object_path\"/>\n" \
- " <arg type=\"a{sa{sv}}\" name=\"interfaces_and_properties\"/>\n" \
- " </signal>\n" \
- " <signal name=\"InterfacesRemoved\">\n" \
- " <arg type=\"o\" name=\"object_path\"/>\n" \
- " <arg type=\"as\" name=\"interfaces\"/>\n" \
- " </signal>\n" \
- " </interface>\n"
diff --git a/src/libsystemd/bus-signature.c b/src/libsystemd/bus-signature.c
deleted file mode 100644
index 1e5bf48..0000000
--- a/src/libsystemd/bus-signature.c
+++ /dev/null
@@ -1,160 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <util.h>
-
-#include "bus-signature.h"
-#include "bus-type.h"
-
-static int signature_element_length_internal(
- const char *s,
- bool allow_dict_entry,
- unsigned array_depth,
- unsigned struct_depth,
- size_t *l) {
-
- int r;
-
- if (!s)
- return -EINVAL;
-
- assert(l);
-
- if (bus_type_is_basic(*s) || *s == SD_BUS_TYPE_VARIANT) {
- *l = 1;
- return 0;
- }
-
- if (*s == SD_BUS_TYPE_ARRAY) {
- size_t t;
-
- if (array_depth >= 32)
- return -EINVAL;
-
- r = signature_element_length_internal(s + 1, true, array_depth+1, struct_depth, &t);
- if (r < 0)
- return r;
-
- *l = t + 1;
- return 0;
- }
-
- if (*s == SD_BUS_TYPE_STRUCT_BEGIN) {
- const char *p = s + 1;
-
- if (struct_depth >= 32)
- return -EINVAL;
-
- while (*p != SD_BUS_TYPE_STRUCT_END) {
- size_t t;
-
- r = signature_element_length_internal(p, false, array_depth, struct_depth+1, &t);
- if (r < 0)
- return r;
-
- p += t;
- }
-
- *l = p - s + 1;
- return 0;
- }
-
- if (*s == SD_BUS_TYPE_DICT_ENTRY_BEGIN && allow_dict_entry) {
- const char *p = s + 1;
- unsigned n = 0;
-
- if (struct_depth >= 32)
- return -EINVAL;
-
- while (*p != SD_BUS_TYPE_DICT_ENTRY_END) {
- size_t t;
-
- if (n == 0 && !bus_type_is_basic(*p))
- return -EINVAL;
-
- r = signature_element_length_internal(p, false, array_depth, struct_depth+1, &t);
- if (r < 0)
- return r;
-
- p += t;
- n++;
- }
-
- if (n != 2)
- return -EINVAL;
-
- *l = p - s + 1;
- return 0;
- }
-
- return -EINVAL;
-}
-
-
-int signature_element_length(const char *s, size_t *l) {
- return signature_element_length_internal(s, true, 0, 0, l);
-}
-
-bool signature_is_single(const char *s, bool allow_dict_entry) {
- int r;
- size_t t;
-
- if (!s)
- return false;
-
- r = signature_element_length_internal(s, allow_dict_entry, 0, 0, &t);
- if (r < 0)
- return false;
-
- return s[t] == 0;
-}
-
-bool signature_is_pair(const char *s) {
-
- if (!s)
- return false;
-
- if (!bus_type_is_basic(*s))
- return false;
-
- return signature_is_single(s + 1, false);
-}
-
-bool signature_is_valid(const char *s, bool allow_dict_entry) {
- const char *p;
- int r;
-
- if (!s)
- return false;
-
- p = s;
- while (*p) {
- size_t t;
-
- r = signature_element_length_internal(p, allow_dict_entry, 0, 0, &t);
- if (r < 0)
- return false;
-
- p += t;
- }
-
- return p - s <= 255;
-}
diff --git a/src/libsystemd/bus-signature.h b/src/libsystemd/bus-signature.h
deleted file mode 100644
index 2e06e30..0000000
--- a/src/libsystemd/bus-signature.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-#include <sys/types.h>
-
-bool signature_is_single(const char *s, bool allow_dict_entry);
-bool signature_is_pair(const char *s);
-bool signature_is_valid(const char *s, bool allow_dict_entry);
-
-int signature_element_length(const char *s, size_t *l);
diff --git a/src/libsystemd/bus-socket.c b/src/libsystemd/bus-socket.c
deleted file mode 100644
index 0c4b6af..0000000
--- a/src/libsystemd/bus-socket.c
+++ /dev/null
@@ -1,1105 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <endian.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/poll.h>
-#include <byteswap.h>
-
-#include "util.h"
-#include "macro.h"
-#include "missing.h"
-#include "strv.h"
-#include "utf8.h"
-#include "sd-daemon.h"
-
-#include "sd-bus.h"
-#include "bus-socket.h"
-#include "bus-internal.h"
-#include "bus-message.h"
-
-#define SNDBUF_SIZE (8*1024*1024)
-
-static void iovec_advance(struct iovec iov[], unsigned *idx, size_t size) {
-
- while (size > 0) {
- struct iovec *i = iov + *idx;
-
- if (i->iov_len > size) {
- i->iov_base = (uint8_t*) i->iov_base + size;
- i->iov_len -= size;
- return;
- }
-
- size -= i->iov_len;
-
- i->iov_base = NULL;
- i->iov_len = 0;
-
- (*idx) ++;
- }
-}
-
-static int append_iovec(sd_bus_message *m, const void *p, size_t sz) {
- assert(m);
- assert(p);
- assert(sz > 0);
-
- m->iovec[m->n_iovec].iov_base = (void*) p;
- m->iovec[m->n_iovec].iov_len = sz;
- m->n_iovec++;
-
- return 0;
-}
-
-static int bus_message_setup_iovec(sd_bus_message *m) {
- struct bus_body_part *part;
- unsigned n, i;
- int r;
-
- assert(m);
- assert(m->sealed);
-
- if (m->n_iovec > 0)
- return 0;
-
- assert(!m->iovec);
-
- n = 1 + m->n_body_parts;
- if (n < ELEMENTSOF(m->iovec_fixed))
- m->iovec = m->iovec_fixed;
- else {
- m->iovec = new(struct iovec, n);
- if (!m->iovec) {
- r = -ENOMEM;
- goto fail;
- }
- }
-
- r = append_iovec(m, m->header, BUS_MESSAGE_BODY_BEGIN(m));
- if (r < 0)
- goto fail;
-
- MESSAGE_FOREACH_PART(part, i, m) {
- r = bus_body_part_map(part);
- if (r < 0)
- goto fail;
-
- r = append_iovec(m, part->data, part->size);
- if (r < 0)
- goto fail;
- }
-
- assert(n == m->n_iovec);
-
- return 0;
-
-fail:
- m->poisoned = true;
- return r;
-}
-
-bool bus_socket_auth_needs_write(sd_bus *b) {
-
- unsigned i;
-
- if (b->auth_index >= ELEMENTSOF(b->auth_iovec))
- return false;
-
- for (i = b->auth_index; i < ELEMENTSOF(b->auth_iovec); i++) {
- struct iovec *j = b->auth_iovec + i;
-
- if (j->iov_len > 0)
- return true;
- }
-
- return false;
-}
-
-static int bus_socket_write_auth(sd_bus *b) {
- ssize_t k;
-
- assert(b);
- assert(b->state == BUS_AUTHENTICATING);
-
- if (!bus_socket_auth_needs_write(b))
- return 0;
-
- if (b->prefer_writev)
- k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index);
- else {
- struct msghdr mh;
- zero(mh);
-
- mh.msg_iov = b->auth_iovec + b->auth_index;
- mh.msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index;
-
- k = sendmsg(b->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
- if (k < 0 && errno == ENOTSOCK) {
- b->prefer_writev = true;
- k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index);
- }
- }
-
- if (k < 0)
- return errno == EAGAIN ? 0 : -errno;
-
- iovec_advance(b->auth_iovec, &b->auth_index, (size_t) k);
- return 1;
-}
-
-static int bus_socket_auth_verify_client(sd_bus *b) {
- char *e, *f, *start;
- sd_id128_t peer;
- unsigned i;
- int r;
-
- assert(b);
-
- /* We expect two response lines: "OK" and possibly
- * "AGREE_UNIX_FD" */
-
- e = memmem(b->rbuffer, b->rbuffer_size, "\r\n", 2);
- if (!e)
- return 0;
-
- if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) {
- f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
- if (!f)
- return 0;
-
- start = f + 2;
- } else {
- f = NULL;
- start = e + 2;
- }
-
- /* Nice! We got all the lines we need. First check the OK
- * line */
-
- if (e - (char*) b->rbuffer != 3 + 32)
- return -EPERM;
-
- if (memcmp(b->rbuffer, "OK ", 3))
- return -EPERM;
-
- b->auth = b->anonymous_auth ? BUS_AUTH_ANONYMOUS : BUS_AUTH_EXTERNAL;
-
- for (i = 0; i < 32; i += 2) {
- int x, y;
-
- x = unhexchar(((char*) b->rbuffer)[3 + i]);
- y = unhexchar(((char*) b->rbuffer)[3 + i + 1]);
-
- if (x < 0 || y < 0)
- return -EINVAL;
-
- peer.bytes[i/2] = ((uint8_t) x << 4 | (uint8_t) y);
- }
-
- if (!sd_id128_equal(b->server_id, SD_ID128_NULL) &&
- !sd_id128_equal(b->server_id, peer))
- return -EPERM;
-
- b->server_id = peer;
-
- /* And possibly check the second line, too */
-
- if (f)
- b->can_fds =
- (f - e == sizeof("\r\nAGREE_UNIX_FD") - 1) &&
- memcmp(e + 2, "AGREE_UNIX_FD", sizeof("AGREE_UNIX_FD") - 1) == 0;
-
- b->rbuffer_size -= (start - (char*) b->rbuffer);
- memmove(b->rbuffer, start, b->rbuffer_size);
-
- r = bus_start_running(b);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static bool line_equals(const char *s, size_t m, const char *line) {
- size_t l;
-
- l = strlen(line);
- if (l != m)
- return false;
-
- return memcmp(s, line, l) == 0;
-}
-
-static bool line_begins(const char *s, size_t m, const char *word) {
- size_t l;
-
- l = strlen(word);
- if (m < l)
- return false;
-
- if (memcmp(s, word, l) != 0)
- return false;
-
- return m == l || (m > l && s[l] == ' ');
-}
-
-static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) {
- _cleanup_free_ char *token = NULL;
-
- if (!b->anonymous_auth)
- return 0;
-
- if (l <= 0)
- return 1;
-
- assert(p[0] == ' ');
- p++; l--;
-
- if (l % 2 != 0)
- return 0;
- token = unhexmem(p, l);
- if (!token)
- return -ENOMEM;
-
- if (memchr(token, 0, l/2))
- return 0;
-
- return !!utf8_is_valid(token);
-}
-
-static int verify_external_token(sd_bus *b, const char *p, size_t l) {
- _cleanup_free_ char *token = NULL;
- uid_t u;
- int r;
-
- /* We don't do any real authentication here. Instead, we if
- * the owner of this bus wanted authentication he should have
- * checked SO_PEERCRED before even creating the bus object. */
-
- if (!b->anonymous_auth && !b->ucred_valid)
- return 0;
-
- if (l <= 0)
- return 1;
-
- assert(p[0] == ' ');
- p++; l--;
-
- if (l % 2 != 0)
- return 0;
-
- token = unhexmem(p, l);
- if (!token)
- return -ENOMEM;
-
- if (memchr(token, 0, l/2))
- return 0;
-
- r = parse_uid(token, &u);
- if (r < 0)
- return 0;
-
- /* We ignore the passed value if anonymous authentication is
- * on anyway. */
- if (!b->anonymous_auth && u != b->ucred.uid)
- return 0;
-
- return 1;
-}
-
-static int bus_socket_auth_write(sd_bus *b, const char *t) {
- char *p;
- size_t l;
-
- assert(b);
- assert(t);
-
- /* We only make use of the first iovec */
- assert(b->auth_index == 0 || b->auth_index == 1);
-
- l = strlen(t);
- p = malloc(b->auth_iovec[0].iov_len + l);
- if (!p)
- return -ENOMEM;
-
- memcpy(p, b->auth_iovec[0].iov_base, b->auth_iovec[0].iov_len);
- memcpy(p + b->auth_iovec[0].iov_len, t, l);
-
- b->auth_iovec[0].iov_base = p;
- b->auth_iovec[0].iov_len += l;
-
- free(b->auth_buffer);
- b->auth_buffer = p;
- b->auth_index = 0;
- return 0;
-}
-
-static int bus_socket_auth_write_ok(sd_bus *b) {
- char t[3 + 32 + 2 + 1];
-
- assert(b);
-
- snprintf(t, sizeof(t), "OK " SD_ID128_FORMAT_STR "\r\n", SD_ID128_FORMAT_VAL(b->server_id));
- char_array_0(t);
-
- return bus_socket_auth_write(b, t);
-}
-
-static int bus_socket_auth_verify_server(sd_bus *b) {
- char *e;
- const char *line;
- size_t l;
- bool processed = false;
- int r;
-
- assert(b);
-
- if (b->rbuffer_size < 1)
- return 0;
-
- /* First char must be a NUL byte */
- if (*(char*) b->rbuffer != 0)
- return -EIO;
-
- if (b->rbuffer_size < 3)
- return 0;
-
- /* Begin with the first line */
- if (b->auth_rbegin <= 0)
- b->auth_rbegin = 1;
-
- for (;;) {
- /* Check if line is complete */
- line = (char*) b->rbuffer + b->auth_rbegin;
- e = memmem(line, b->rbuffer_size - b->auth_rbegin, "\r\n", 2);
- if (!e)
- return processed;
-
- l = e - line;
-
- if (line_begins(line, l, "AUTH ANONYMOUS")) {
-
- r = verify_anonymous_token(b, line + 14, l - 14);
- if (r < 0)
- return r;
- if (r == 0)
- r = bus_socket_auth_write(b, "REJECTED\r\n");
- else {
- b->auth = BUS_AUTH_ANONYMOUS;
- r = bus_socket_auth_write_ok(b);
- }
-
- } else if (line_begins(line, l, "AUTH EXTERNAL")) {
-
- r = verify_external_token(b, line + 13, l - 13);
- if (r < 0)
- return r;
- if (r == 0)
- r = bus_socket_auth_write(b, "REJECTED\r\n");
- else {
- b->auth = BUS_AUTH_EXTERNAL;
- r = bus_socket_auth_write_ok(b);
- }
-
- } else if (line_begins(line, l, "AUTH"))
- r = bus_socket_auth_write(b, "REJECTED EXTERNAL ANONYMOUS\r\n");
- else if (line_equals(line, l, "CANCEL") ||
- line_begins(line, l, "ERROR")) {
-
- b->auth = _BUS_AUTH_INVALID;
- r = bus_socket_auth_write(b, "REJECTED\r\n");
-
- } else if (line_equals(line, l, "BEGIN")) {
-
- if (b->auth == _BUS_AUTH_INVALID)
- r = bus_socket_auth_write(b, "ERROR\r\n");
- else {
- /* We can't leave from the auth phase
- * before we haven't written
- * everything queued, so let's check
- * that */
-
- if (bus_socket_auth_needs_write(b))
- return 1;
-
- b->rbuffer_size -= (e + 2 - (char*) b->rbuffer);
- memmove(b->rbuffer, e + 2, b->rbuffer_size);
- return bus_start_running(b);
- }
-
- } else if (line_begins(line, l, "DATA")) {
-
- if (b->auth == _BUS_AUTH_INVALID)
- r = bus_socket_auth_write(b, "ERROR\r\n");
- else {
- if (b->auth == BUS_AUTH_ANONYMOUS)
- r = verify_anonymous_token(b, line + 4, l - 4);
- else
- r = verify_external_token(b, line + 4, l - 4);
-
- if (r < 0)
- return r;
- if (r == 0) {
- b->auth = _BUS_AUTH_INVALID;
- r = bus_socket_auth_write(b, "REJECTED\r\n");
- } else
- r = bus_socket_auth_write_ok(b);
- }
- } else if (line_equals(line, l, "NEGOTIATE_UNIX_FD")) {
- if (b->auth == _BUS_AUTH_INVALID || !(b->hello_flags & KDBUS_HELLO_ACCEPT_FD))
- r = bus_socket_auth_write(b, "ERROR\r\n");
- else {
- b->can_fds = true;
- r = bus_socket_auth_write(b, "AGREE_UNIX_FD\r\n");
- }
- } else
- r = bus_socket_auth_write(b, "ERROR\r\n");
-
- if (r < 0)
- return r;
-
- b->auth_rbegin = e + 2 - (char*) b->rbuffer;
-
- processed = true;
- }
-}
-
-static int bus_socket_auth_verify(sd_bus *b) {
- assert(b);
-
- if (b->is_server)
- return bus_socket_auth_verify_server(b);
- else
- return bus_socket_auth_verify_client(b);
-}
-
-static int bus_socket_read_auth(sd_bus *b) {
- struct msghdr mh;
- struct iovec iov;
- size_t n;
- ssize_t k;
- int r;
- void *p;
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX) +
- CMSG_SPACE(sizeof(struct ucred)) +
- CMSG_SPACE(NAME_MAX)]; /*selinux label */
- } control;
- struct cmsghdr *cmsg;
- bool handle_cmsg = false;
-
- assert(b);
- assert(b->state == BUS_AUTHENTICATING);
-
- r = bus_socket_auth_verify(b);
- if (r != 0)
- return r;
-
- n = MAX(256u, b->rbuffer_size * 2);
-
- if (n > BUS_AUTH_SIZE_MAX)
- n = BUS_AUTH_SIZE_MAX;
-
- if (b->rbuffer_size >= n)
- return -ENOBUFS;
-
- p = realloc(b->rbuffer, n);
- if (!p)
- return -ENOMEM;
-
- b->rbuffer = p;
-
- zero(iov);
- iov.iov_base = (uint8_t*) b->rbuffer + b->rbuffer_size;
- iov.iov_len = n - b->rbuffer_size;
-
- if (b->prefer_readv)
- k = readv(b->input_fd, &iov, 1);
- else {
- zero(mh);
- mh.msg_iov = &iov;
- mh.msg_iovlen = 1;
- mh.msg_control = &control;
- mh.msg_controllen = sizeof(control);
-
- k = recvmsg(b->input_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
- if (k < 0 && errno == ENOTSOCK) {
- b->prefer_readv = true;
- k = readv(b->input_fd, &iov, 1);
- } else
- handle_cmsg = true;
- }
- if (k < 0)
- return errno == EAGAIN ? 0 : -errno;
- if (k == 0)
- return -ECONNRESET;
-
- b->rbuffer_size += k;
-
- if (handle_cmsg) {
- for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS) {
- int j;
-
- /* Whut? We received fds during the auth
- * protocol? Somebody is playing games with
- * us. Close them all, and fail */
- j = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
- close_many((int*) CMSG_DATA(cmsg), j);
- return -EIO;
-
- } else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_CREDENTIALS &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
-
- /* Ignore bogus data, which we might
- * get on socketpair() sockets */
- if (((struct ucred*) CMSG_DATA(cmsg))->pid != 0) {
- memcpy(&b->ucred, CMSG_DATA(cmsg), sizeof(struct ucred));
- b->ucred_valid = true;
- }
-
- } else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_SECURITY) {
-
- size_t l;
-
- l = cmsg->cmsg_len - CMSG_LEN(0);
- if (l > 0) {
- memcpy(&b->label, CMSG_DATA(cmsg), l);
- b->label[l] = 0;
- }
- }
- }
- }
-
- r = bus_socket_auth_verify(b);
- if (r != 0)
- return r;
-
- return 1;
-}
-
-void bus_socket_setup(sd_bus *b) {
- int enable;
-
- assert(b);
-
- /* Enable SO_PASSCRED + SO_PASSEC. We try this on any
- * socket, just in case. */
- enable = !b->bus_client;
- setsockopt(b->input_fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable));
-
- enable = !b->bus_client && (b->attach_flags & KDBUS_ATTACH_SECLABEL);
- setsockopt(b->input_fd, SOL_SOCKET, SO_PASSSEC, &enable, sizeof(enable));
-
- /* Increase the buffers to 8 MB */
- fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE);
- fd_inc_sndbuf(b->output_fd, SNDBUF_SIZE);
-
- b->is_kernel = false;
- b->message_version = 1;
- b->message_endian = 0;
-}
-
-static void bus_get_peercred(sd_bus *b) {
- assert(b);
-
- /* Get the peer for socketpair() sockets */
- b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0;
-}
-
-static int bus_socket_start_auth_client(sd_bus *b) {
- size_t l;
- const char *auth_suffix, *auth_prefix;
-
- assert(b);
-
- if (b->anonymous_auth) {
- auth_prefix = "\0AUTH ANONYMOUS ";
-
- /* For ANONYMOUS auth we send some arbitrary "trace" string */
- l = 9;
- b->auth_buffer = hexmem("anonymous", l);
- } else {
- char text[20 + 1]; /* enough space for a 64bit integer plus NUL */
-
- auth_prefix = "\0AUTH EXTERNAL ";
-
- snprintf(text, sizeof(text), "%lu", (unsigned long) geteuid());
- char_array_0(text);
-
- l = strlen(text);
- b->auth_buffer = hexmem(text, l);
- }
-
- if (!b->auth_buffer)
- return -ENOMEM;
-
- if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD)
- auth_suffix = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n";
- else
- auth_suffix = "\r\nBEGIN\r\n";
-
- b->auth_iovec[0].iov_base = (void*) auth_prefix;
- b->auth_iovec[0].iov_len = 1 + strlen(auth_prefix + 1);
- b->auth_iovec[1].iov_base = (void*) b->auth_buffer;
- b->auth_iovec[1].iov_len = l * 2;
- b->auth_iovec[2].iov_base = (void*) auth_suffix;
- b->auth_iovec[2].iov_len = strlen(auth_suffix);
-
- return bus_socket_write_auth(b);
-}
-
-int bus_socket_start_auth(sd_bus *b) {
- assert(b);
-
- bus_get_peercred(b);
-
- b->state = BUS_AUTHENTICATING;
- b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_DEFAULT_TIMEOUT;
-
- if (sd_is_socket(b->input_fd, AF_UNIX, 0, 0) <= 0)
- b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD;
-
- if (b->output_fd != b->input_fd)
- if (sd_is_socket(b->output_fd, AF_UNIX, 0, 0) <= 0)
- b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD;
-
- if (b->is_server)
- return bus_socket_read_auth(b);
- else
- return bus_socket_start_auth_client(b);
-}
-
-int bus_socket_connect(sd_bus *b) {
- int r;
-
- assert(b);
- assert(b->input_fd < 0);
- assert(b->output_fd < 0);
- assert(b->sockaddr.sa.sa_family != AF_UNSPEC);
-
- b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (b->input_fd < 0)
- return -errno;
-
- b->output_fd = b->input_fd;
-
- bus_socket_setup(b);
-
- r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
- if (r < 0) {
- if (errno == EINPROGRESS)
- return 1;
-
- return -errno;
- }
-
- return bus_socket_start_auth(b);
-}
-
-int bus_socket_exec(sd_bus *b) {
- int s[2], r;
- pid_t pid;
-
- assert(b);
- assert(b->input_fd < 0);
- assert(b->output_fd < 0);
- assert(b->exec_path);
-
- r = socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, s);
- if (r < 0)
- return -errno;
-
- pid = fork();
- if (pid < 0) {
- close_pipe(s);
- return -errno;
- }
- if (pid == 0) {
- /* Child */
-
- reset_all_signal_handlers();
-
- close_all_fds(s+1, 1);
-
- assert_se(dup3(s[1], STDIN_FILENO, 0) == STDIN_FILENO);
- assert_se(dup3(s[1], STDOUT_FILENO, 0) == STDOUT_FILENO);
-
- if (s[1] != STDIN_FILENO && s[1] != STDOUT_FILENO)
- close_nointr_nofail(s[1]);
-
- fd_cloexec(STDIN_FILENO, false);
- fd_cloexec(STDOUT_FILENO, false);
- fd_nonblock(STDIN_FILENO, false);
- fd_nonblock(STDOUT_FILENO, false);
-
- if (b->exec_argv)
- execvp(b->exec_path, b->exec_argv);
- else {
- const char *argv[] = { b->exec_path, NULL };
- execvp(b->exec_path, (char**) argv);
- }
-
- _exit(EXIT_FAILURE);
- }
-
- close_nointr_nofail(s[1]);
- b->output_fd = b->input_fd = s[0];
-
- bus_socket_setup(b);
-
- return bus_socket_start_auth(b);
-}
-
-int bus_socket_take_fd(sd_bus *b) {
- assert(b);
-
- bus_socket_setup(b);
-
- return bus_socket_start_auth(b);
-}
-
-int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
- struct iovec *iov;
- ssize_t k;
- size_t n;
- unsigned j;
- int r;
-
- assert(bus);
- assert(m);
- assert(idx);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- if (*idx >= BUS_MESSAGE_SIZE(m))
- return 0;
-
- r = bus_message_setup_iovec(m);
- if (r < 0)
- return r;
-
- n = m->n_iovec * sizeof(struct iovec);
- iov = alloca(n);
- memcpy(iov, m->iovec, n);
-
- j = 0;
- iovec_advance(iov, &j, *idx);
-
- if (bus->prefer_writev)
- k = writev(bus->output_fd, iov, m->n_iovec);
- else {
- struct msghdr mh;
- zero(mh);
-
- if (m->n_fds > 0) {
- struct cmsghdr *control;
- control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds));
-
- mh.msg_control = control;
- control->cmsg_level = SOL_SOCKET;
- control->cmsg_type = SCM_RIGHTS;
- mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds);
- memcpy(CMSG_DATA(control), m->fds, sizeof(int) * m->n_fds);
- }
-
- mh.msg_iov = iov;
- mh.msg_iovlen = m->n_iovec;
-
- k = sendmsg(bus->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
- if (k < 0 && errno == ENOTSOCK) {
- bus->prefer_writev = true;
- k = writev(bus->output_fd, iov, m->n_iovec);
- }
- }
-
- if (k < 0)
- return errno == EAGAIN ? 0 : -errno;
-
- *idx += (size_t) k;
- return 1;
-}
-
-static int bus_socket_read_message_need(sd_bus *bus, size_t *need) {
- uint32_t a, b;
- uint8_t e;
- uint64_t sum;
-
- assert(bus);
- assert(need);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- if (bus->rbuffer_size < sizeof(struct bus_header)) {
- *need = sizeof(struct bus_header) + 8;
-
- /* Minimum message size:
- *
- * Header +
- *
- * Method Call: +2 string headers
- * Signal: +3 string headers
- * Method Error: +1 string headers
- * +1 uint32 headers
- * Method Reply: +1 uint32 headers
- *
- * A string header is at least 9 bytes
- * A uint32 header is at least 8 bytes
- *
- * Hence the minimum message size of a valid message
- * is header + 8 bytes */
-
- return 0;
- }
-
- a = ((const uint32_t*) bus->rbuffer)[1];
- b = ((const uint32_t*) bus->rbuffer)[3];
-
- e = ((const uint8_t*) bus->rbuffer)[0];
- if (e == BUS_LITTLE_ENDIAN) {
- a = le32toh(a);
- b = le32toh(b);
- } else if (e == BUS_BIG_ENDIAN) {
- a = be32toh(a);
- b = be32toh(b);
- } else
- return -EBADMSG;
-
- sum = (uint64_t) sizeof(struct bus_header) + (uint64_t) ALIGN_TO(b, 8) + (uint64_t) a;
- if (sum >= BUS_MESSAGE_SIZE_MAX)
- return -ENOBUFS;
-
- *need = (size_t) sum;
- return 0;
-}
-
-static int bus_socket_make_message(sd_bus *bus, size_t size) {
- sd_bus_message *t;
- void *b;
- int r;
-
- assert(bus);
- assert(bus->rbuffer_size >= size);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- r = bus_rqueue_make_room(bus);
- if (r < 0)
- return r;
-
- if (bus->rbuffer_size > size) {
- b = memdup((const uint8_t*) bus->rbuffer + size,
- bus->rbuffer_size - size);
- if (!b)
- return -ENOMEM;
- } else
- b = NULL;
-
- r = bus_message_from_malloc(bus,
- bus->rbuffer, size,
- bus->fds, bus->n_fds,
- !bus->bus_client && bus->ucred_valid ? &bus->ucred : NULL,
- !bus->bus_client && bus->label[0] ? bus->label : NULL,
- &t);
- if (r < 0) {
- free(b);
- return r;
- }
-
- bus->rbuffer = b;
- bus->rbuffer_size -= size;
-
- bus->fds = NULL;
- bus->n_fds = 0;
-
- bus->rqueue[bus->rqueue_size++] = t;
-
- return 1;
-}
-
-int bus_socket_read_message(sd_bus *bus) {
- struct msghdr mh;
- struct iovec iov;
- ssize_t k;
- size_t need;
- int r;
- void *b;
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX) +
- CMSG_SPACE(sizeof(struct ucred)) +
- CMSG_SPACE(NAME_MAX)]; /*selinux label */
- } control;
- struct cmsghdr *cmsg;
- bool handle_cmsg = false;
-
- assert(bus);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- r = bus_socket_read_message_need(bus, &need);
- if (r < 0)
- return r;
-
- if (bus->rbuffer_size >= need)
- return bus_socket_make_message(bus, need);
-
- b = realloc(bus->rbuffer, need);
- if (!b)
- return -ENOMEM;
-
- bus->rbuffer = b;
-
- zero(iov);
- iov.iov_base = (uint8_t*) bus->rbuffer + bus->rbuffer_size;
- iov.iov_len = need - bus->rbuffer_size;
-
- if (bus->prefer_readv)
- k = readv(bus->input_fd, &iov, 1);
- else {
- zero(mh);
- mh.msg_iov = &iov;
- mh.msg_iovlen = 1;
- mh.msg_control = &control;
- mh.msg_controllen = sizeof(control);
-
- k = recvmsg(bus->input_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
- if (k < 0 && errno == ENOTSOCK) {
- bus->prefer_readv = true;
- k = readv(bus->input_fd, &iov, 1);
- } else
- handle_cmsg = true;
- }
- if (k < 0)
- return errno == EAGAIN ? 0 : -errno;
- if (k == 0)
- return -ECONNRESET;
-
- bus->rbuffer_size += k;
-
- if (handle_cmsg) {
- for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS) {
- int n, *f;
-
- n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
-
- if (!bus->can_fds) {
- /* Whut? We received fds but this
- * isn't actually enabled? Close them,
- * and fail */
-
- close_many((int*) CMSG_DATA(cmsg), n);
- return -EIO;
- }
-
- f = realloc(bus->fds, sizeof(int) + (bus->n_fds + n));
- if (!f) {
- close_many((int*) CMSG_DATA(cmsg), n);
- return -ENOMEM;
- }
-
- memcpy(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int));
- bus->fds = f;
- bus->n_fds += n;
- } else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_CREDENTIALS &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
-
- /* Ignore bogus data, which we might
- * get on socketpair() sockets */
- if (((struct ucred*) CMSG_DATA(cmsg))->pid != 0) {
- memcpy(&bus->ucred, CMSG_DATA(cmsg), sizeof(struct ucred));
- bus->ucred_valid = true;
- }
-
- } else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_SECURITY) {
-
- size_t l;
- l = cmsg->cmsg_len - CMSG_LEN(0);
- if (l > 0) {
- memcpy(&bus->label, CMSG_DATA(cmsg), l);
- bus->label[l] = 0;
- }
- }
- }
- }
-
- r = bus_socket_read_message_need(bus, &need);
- if (r < 0)
- return r;
-
- if (bus->rbuffer_size >= need)
- return bus_socket_make_message(bus, need);
-
- return 1;
-}
-
-int bus_socket_process_opening(sd_bus *b) {
- int error = 0;
- socklen_t slen = sizeof(error);
- struct pollfd p = {
- .fd = b->output_fd,
- .events = POLLOUT,
- };
- int r;
-
- assert(b->state == BUS_OPENING);
-
- r = poll(&p, 1, 0);
- if (r < 0)
- return -errno;
-
- if (!(p.revents & (POLLOUT|POLLERR|POLLHUP)))
- return 0;
-
- r = getsockopt(b->output_fd, SOL_SOCKET, SO_ERROR, &error, &slen);
- if (r < 0)
- b->last_connect_error = errno;
- else if (error != 0)
- b->last_connect_error = error;
- else if (p.revents & (POLLERR|POLLHUP))
- b->last_connect_error = ECONNREFUSED;
- else
- return bus_socket_start_auth(b);
-
- return bus_next_address(b);
-}
-
-int bus_socket_process_authenticating(sd_bus *b) {
- int r;
-
- assert(b);
- assert(b->state == BUS_AUTHENTICATING);
-
- if (now(CLOCK_MONOTONIC) >= b->auth_timeout)
- return -ETIMEDOUT;
-
- r = bus_socket_write_auth(b);
- if (r != 0)
- return r;
-
- return bus_socket_read_auth(b);
-}
diff --git a/src/libsystemd/bus-socket.h b/src/libsystemd/bus-socket.h
deleted file mode 100644
index 5a1c7d4..0000000
--- a/src/libsystemd/bus-socket.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "sd-bus.h"
-
-void bus_socket_setup(sd_bus *b);
-
-int bus_socket_connect(sd_bus *b);
-int bus_socket_exec(sd_bus *b);
-int bus_socket_take_fd(sd_bus *b);
-int bus_socket_start_auth(sd_bus *b);
-
-int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx);
-int bus_socket_read_message(sd_bus *bus);
-
-int bus_socket_process_opening(sd_bus *b);
-int bus_socket_process_authenticating(sd_bus *b);
-
-bool bus_socket_auth_needs_write(sd_bus *b);
diff --git a/src/libsystemd/bus-type.c b/src/libsystemd/bus-type.c
deleted file mode 100644
index b7914d1..0000000
--- a/src/libsystemd/bus-type.c
+++ /dev/null
@@ -1,179 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-#include "bus-type.h"
-
-bool bus_type_is_valid(char c) {
- static const char valid[] = {
- SD_BUS_TYPE_BYTE,
- SD_BUS_TYPE_BOOLEAN,
- SD_BUS_TYPE_INT16,
- SD_BUS_TYPE_UINT16,
- SD_BUS_TYPE_INT32,
- SD_BUS_TYPE_UINT32,
- SD_BUS_TYPE_INT64,
- SD_BUS_TYPE_UINT64,
- SD_BUS_TYPE_DOUBLE,
- SD_BUS_TYPE_STRING,
- SD_BUS_TYPE_OBJECT_PATH,
- SD_BUS_TYPE_SIGNATURE,
- SD_BUS_TYPE_ARRAY,
- SD_BUS_TYPE_VARIANT,
- SD_BUS_TYPE_STRUCT,
- SD_BUS_TYPE_DICT_ENTRY,
- SD_BUS_TYPE_UNIX_FD
- };
-
- return !!memchr(valid, c, sizeof(valid));
-}
-
-bool bus_type_is_valid_in_signature(char c) {
- static const char valid[] = {
- SD_BUS_TYPE_BYTE,
- SD_BUS_TYPE_BOOLEAN,
- SD_BUS_TYPE_INT16,
- SD_BUS_TYPE_UINT16,
- SD_BUS_TYPE_INT32,
- SD_BUS_TYPE_UINT32,
- SD_BUS_TYPE_INT64,
- SD_BUS_TYPE_UINT64,
- SD_BUS_TYPE_DOUBLE,
- SD_BUS_TYPE_STRING,
- SD_BUS_TYPE_OBJECT_PATH,
- SD_BUS_TYPE_SIGNATURE,
- SD_BUS_TYPE_ARRAY,
- SD_BUS_TYPE_VARIANT,
- SD_BUS_TYPE_STRUCT_BEGIN,
- SD_BUS_TYPE_STRUCT_END,
- SD_BUS_TYPE_DICT_ENTRY_BEGIN,
- SD_BUS_TYPE_DICT_ENTRY_END,
- SD_BUS_TYPE_UNIX_FD
- };
-
- return !!memchr(valid, c, sizeof(valid));
-}
-
-bool bus_type_is_basic(char c) {
- static const char valid[] = {
- SD_BUS_TYPE_BYTE,
- SD_BUS_TYPE_BOOLEAN,
- SD_BUS_TYPE_INT16,
- SD_BUS_TYPE_UINT16,
- SD_BUS_TYPE_INT32,
- SD_BUS_TYPE_UINT32,
- SD_BUS_TYPE_INT64,
- SD_BUS_TYPE_UINT64,
- SD_BUS_TYPE_DOUBLE,
- SD_BUS_TYPE_STRING,
- SD_BUS_TYPE_OBJECT_PATH,
- SD_BUS_TYPE_SIGNATURE,
- SD_BUS_TYPE_UNIX_FD
- };
-
- return !!memchr(valid, c, sizeof(valid));
-}
-
-bool bus_type_is_trivial(char c) {
- static const char valid[] = {
- SD_BUS_TYPE_BYTE,
- SD_BUS_TYPE_BOOLEAN,
- SD_BUS_TYPE_INT16,
- SD_BUS_TYPE_UINT16,
- SD_BUS_TYPE_INT32,
- SD_BUS_TYPE_UINT32,
- SD_BUS_TYPE_INT64,
- SD_BUS_TYPE_UINT64,
- SD_BUS_TYPE_DOUBLE
- };
-
- return !!memchr(valid, c, sizeof(valid));
-}
-
-bool bus_type_is_container(char c) {
- static const char valid[] = {
- SD_BUS_TYPE_ARRAY,
- SD_BUS_TYPE_VARIANT,
- SD_BUS_TYPE_STRUCT,
- SD_BUS_TYPE_DICT_ENTRY
- };
-
- return !!memchr(valid, c, sizeof(valid));
-}
-
-int bus_type_get_alignment(char c) {
-
- switch (c) {
- case SD_BUS_TYPE_BYTE:
- case SD_BUS_TYPE_SIGNATURE:
- case SD_BUS_TYPE_VARIANT:
- return 1;
-
- case SD_BUS_TYPE_INT16:
- case SD_BUS_TYPE_UINT16:
- return 2;
-
- case SD_BUS_TYPE_BOOLEAN:
- case SD_BUS_TYPE_INT32:
- case SD_BUS_TYPE_UINT32:
- case SD_BUS_TYPE_STRING:
- case SD_BUS_TYPE_OBJECT_PATH:
- case SD_BUS_TYPE_ARRAY:
- case SD_BUS_TYPE_UNIX_FD:
- return 4;
-
- case SD_BUS_TYPE_INT64:
- case SD_BUS_TYPE_UINT64:
- case SD_BUS_TYPE_DOUBLE:
- case SD_BUS_TYPE_STRUCT:
- case SD_BUS_TYPE_STRUCT_BEGIN:
- case SD_BUS_TYPE_DICT_ENTRY:
- case SD_BUS_TYPE_DICT_ENTRY_BEGIN:
- return 8;
- }
-
- return -EINVAL;
-}
-
-int bus_type_get_size(char c) {
-
- switch (c) {
- case SD_BUS_TYPE_BYTE:
- return 1;
-
- case SD_BUS_TYPE_INT16:
- case SD_BUS_TYPE_UINT16:
- return 2;
-
- case SD_BUS_TYPE_BOOLEAN:
- case SD_BUS_TYPE_INT32:
- case SD_BUS_TYPE_UINT32:
- case SD_BUS_TYPE_UNIX_FD:
- return 4;
-
- case SD_BUS_TYPE_INT64:
- case SD_BUS_TYPE_UINT64:
- case SD_BUS_TYPE_DOUBLE:
- return 8;
- }
-
- return -EINVAL;
-}
diff --git a/src/libsystemd/bus-type.h b/src/libsystemd/bus-type.h
deleted file mode 100644
index 2e423bb..0000000
--- a/src/libsystemd/bus-type.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-
-#include "sd-bus.h"
-#include "sd-bus-protocol.h"
-
-bool bus_type_is_valid(char c) _const_;
-bool bus_type_is_valid_in_signature(char c) _const_;
-bool bus_type_is_basic(char c) _const_;
-bool bus_type_is_trivial(char c) _const_;
-bool bus_type_is_container(char c) _const_;
-
-int bus_type_get_alignment(char c) _const_;
-int bus_type_get_size(char c) _const_;
diff --git a/src/libsystemd/bus-util.c b/src/libsystemd/bus-util.c
deleted file mode 100644
index a468bca..0000000
--- a/src/libsystemd/bus-util.c
+++ /dev/null
@@ -1,1232 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/socket.h>
-#include <sys/capability.h>
-
-#include "util.h"
-#include "strv.h"
-#include "macro.h"
-#include "def.h"
-#include "missing.h"
-
-#include "sd-event.h"
-#include "sd-bus.h"
-#include "bus-error.h"
-#include "bus-message.h"
-#include "bus-util.h"
-#include "bus-internal.h"
-
-static int name_owner_change_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
- sd_event *e = userdata;
-
- assert(bus);
- assert(m);
- assert(e);
-
- sd_event_exit(e, 0);
- return 1;
-}
-
-int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
- _cleanup_free_ char *match = NULL;
- const char *unique;
- int r;
-
- assert(e);
- assert(bus);
- assert(name);
-
- /* We unregister the name here and then wait for the
- * NameOwnerChanged signal for this event to arrive before we
- * quit. We do this in order to make sure that any queued
- * requests are still processed before we really exit. */
-
- r = sd_bus_get_unique_name(bus, &unique);
- if (r < 0)
- return r;
-
- r = asprintf(&match,
- "sender='org.freedesktop.DBus',"
- "type='signal',"
- "interface='org.freedesktop.DBus',"
- "member='NameOwnerChanged',"
- "path='/org/freedesktop/DBus',"
- "arg0='%s',"
- "arg1='%s',"
- "arg2=''", name, unique);
- if (r < 0)
- return -ENOMEM;
-
- r = sd_bus_add_match(bus, match, name_owner_change_callback, e);
- if (r < 0)
- return r;
-
- r = sd_bus_release_name(bus, name);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int bus_event_loop_with_idle(
- sd_event *e,
- sd_bus *bus,
- const char *name,
- usec_t timeout,
- check_idle_t check_idle,
- void *userdata) {
- bool exiting = false;
- int r, code;
-
- assert(e);
- assert(bus);
- assert(name);
-
- for (;;) {
- bool idle;
-
- r = sd_event_get_state(e);
- if (r < 0)
- return r;
- if (r == SD_EVENT_FINISHED)
- break;
-
- if (check_idle)
- idle = check_idle(userdata);
- else
- idle = true;
-
- r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout);
- if (r < 0)
- return r;
-
- if (r == 0 && !exiting) {
- r = bus_async_unregister_and_exit(e, bus, name);
- if (r < 0)
- return r;
-
- exiting = true;
- }
- }
-
- r = sd_event_get_exit_code(e, &code);
- if (r < 0)
- return r;
-
- return code;
-}
-
-int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *rep = NULL;
- int r, has_owner = 0;
-
- assert(c);
- assert(name);
-
- r = sd_bus_call_method(c,
- "org.freedesktop.DBus",
- "/org/freedesktop/dbus",
- "org.freedesktop.DBus",
- "NameHasOwner",
- error,
- &rep,
- "s",
- name);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_basic(rep, 'b', &has_owner);
- if (r < 0)
- return sd_bus_error_set_errno(error, r);
-
- return has_owner;
-}
-
-int bus_verify_polkit(
- sd_bus *bus,
- sd_bus_message *m,
- const char *action,
- bool interactive,
- bool *_challenge,
- sd_bus_error *e) {
-
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
- uid_t uid;
- int r;
-
- assert(bus);
- assert(m);
- assert(action);
-
- r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
- if (r < 0)
- return r;
-
- r = sd_bus_creds_get_uid(creds, &uid);
- if (r < 0)
- return r;
-
- if (uid == 0)
- return 1;
-
-#ifdef ENABLE_POLKIT
- else {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- int authorized = false, challenge = false;
- const char *sender;
-
- sender = sd_bus_message_get_sender(m);
- if (!sender)
- return -EBADMSG;
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.PolicyKit1",
- "/org/freedesktop/PolicyKit1/Authority",
- "org.freedesktop.PolicyKit1.Authority",
- "CheckAuthorization",
- e,
- &reply,
- "(sa{sv})sa{ss}us",
- "system-bus-name", 1, "name", "s", sender,
- action,
- 0,
- interactive ? 1 : 0,
- "");
-
- if (r < 0) {
- /* Treat no PK available as access denied */
- if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
- sd_bus_error_free(e);
- return -EACCES;
- }
-
- return r;
- }
-
- r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
- if (r < 0)
- return r;
-
- if (authorized)
- return 1;
-
- if (_challenge) {
- *_challenge = challenge;
- return 0;
- }
- }
-#endif
-
- return -EACCES;
-}
-
-#ifdef ENABLE_POLKIT
-
-typedef struct AsyncPolkitQuery {
- sd_bus_message *request, *reply;
- sd_bus_message_handler_t callback;
- void *userdata;
- uint64_t serial;
- Hashmap *registry;
-} AsyncPolkitQuery;
-
-static void async_polkit_query_free(sd_bus *b, AsyncPolkitQuery *q) {
-
- if (!q)
- return;
-
- if (q->serial > 0 && b)
- sd_bus_call_async_cancel(b, q->serial);
-
- if (q->registry && q->request)
- hashmap_remove(q->registry, q->request);
-
- sd_bus_message_unref(q->request);
- sd_bus_message_unref(q->reply);
-
- free(q);
-}
-
-static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
- AsyncPolkitQuery *q = userdata;
- int r;
-
- assert(bus);
- assert(reply);
- assert(q);
-
- q->reply = sd_bus_message_ref(reply);
- q->serial = 0;
-
- r = sd_bus_message_rewind(q->request, true);
- if (r < 0) {
- r = sd_bus_reply_method_errno(q->request, r, NULL);
- goto finish;
- }
-
- r = q->callback(bus, q->request, q->userdata, &error_buffer);
- r = bus_maybe_reply_error(q->request, r, &error_buffer);
-
-finish:
- async_polkit_query_free(bus, q);
- return r;
-}
-
-#endif
-
-int bus_verify_polkit_async(
- sd_bus *bus,
- Hashmap **registry,
- sd_bus_message *m,
- const char *action,
- bool interactive,
- sd_bus_error *error,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
-#ifdef ENABLE_POLKIT
- _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
- AsyncPolkitQuery *q;
- const char *sender;
-#endif
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
- uid_t uid;
- int r;
-
- assert(bus);
- assert(registry);
- assert(m);
- assert(action);
-
-#ifdef ENABLE_POLKIT
- q = hashmap_get(*registry, m);
- if (q) {
- int authorized, challenge;
-
- /* This is the second invocation of this function, and
- * there's already a response from polkit, let's
- * process it */
- assert(q->reply);
-
- if (sd_bus_message_is_method_error(q->reply, NULL)) {
- const sd_bus_error *e;
-
- /* Copy error from polkit reply */
- e = sd_bus_message_get_error(q->reply);
- sd_bus_error_copy(error, e);
-
- /* Treat no PK available as access denied */
- if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN))
- return -EACCES;
-
- return -sd_bus_error_get_errno(e);
- }
-
- r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
- if (r >= 0)
- r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
-
- if (r < 0)
- return r;
-
- if (authorized)
- return 1;
-
- return -EACCES;
- }
-#endif
-
- r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
- if (r < 0)
- return r;
-
- r = sd_bus_creds_get_uid(creds, &uid);
- if (r < 0)
- return r;
-
- if (uid == 0)
- return 1;
-
-#ifdef ENABLE_POLKIT
- sender = sd_bus_message_get_sender(m);
- if (!sender)
- return -EBADMSG;
-
- r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
- if (r < 0)
- return r;
-
- r = sd_bus_message_new_method_call(
- bus,
- "org.freedesktop.PolicyKit1",
- "/org/freedesktop/PolicyKit1/Authority",
- "org.freedesktop.PolicyKit1.Authority",
- "CheckAuthorization",
- &pk);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(
- pk,
- "(sa{sv})sa{ss}us",
- "system-bus-name", 1, "name", "s", sender,
- action,
- 0,
- interactive ? 1 : 0,
- NULL);
- if (r < 0)
- return r;
-
- q = new0(AsyncPolkitQuery, 1);
- if (!q)
- return -ENOMEM;
-
- q->request = sd_bus_message_ref(m);
- q->callback = callback;
- q->userdata = userdata;
-
- r = hashmap_put(*registry, m, q);
- if (r < 0) {
- async_polkit_query_free(bus, q);
- return r;
- }
-
- q->registry = *registry;
-
- r = sd_bus_call_async(bus, pk, async_polkit_callback, q, 0, &q->serial);
- if (r < 0) {
- async_polkit_query_free(bus, q);
- return r;
- }
-
- return 0;
-#endif
-
- return -EACCES;
-}
-
-void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) {
-#ifdef ENABLE_POLKIT
- AsyncPolkitQuery *q;
-
- while ((q = hashmap_steal_first(registry)))
- async_polkit_query_free(bus, q);
-
- hashmap_free(registry);
-#endif
-}
-
-int bus_check_peercred(sd_bus *c) {
- struct ucred ucred;
- socklen_t l;
- int fd;
-
- assert(c);
-
- fd = sd_bus_get_fd(c);
- if (fd < 0)
- return fd;
-
- l = sizeof(struct ucred);
- if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
- return -errno;
-
- if (l != sizeof(struct ucred))
- return -E2BIG;
-
- if (ucred.uid != 0 && ucred.uid != geteuid())
- return -EPERM;
-
- return 1;
-}
-
-int bus_open_system_systemd(sd_bus **_bus) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
- int r;
-
- assert(_bus);
-
- if (geteuid() != 0)
- return sd_bus_open_system(_bus);
-
- /* If we are root and kdbus is not available, then let's talk
- * directly to the system instance, instead of going via the
- * bus */
-
-#ifdef ENABLE_KDBUS
- r = sd_bus_new(&bus);
- if (r < 0)
- return r;
-
- r = sd_bus_set_address(bus, KERNEL_SYSTEM_BUS_PATH);
- if (r < 0)
- return r;
-
- bus->bus_client = true;
-
- r = sd_bus_start(bus);
- if (r >= 0) {
- *_bus = bus;
- bus = NULL;
- return 0;
- }
-
- bus = sd_bus_unref(bus);
-#endif
-
- r = sd_bus_new(&bus);
- if (r < 0)
- return r;
-
- r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
- if (r < 0)
- return r;
-
- r = sd_bus_start(bus);
- if (r < 0)
- return sd_bus_open_system(_bus);
-
- r = bus_check_peercred(bus);
- if (r < 0)
- return r;
-
- *_bus = bus;
- bus = NULL;
-
- return 0;
-}
-
-int bus_open_user_systemd(sd_bus **_bus) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
- _cleanup_free_ char *ee = NULL;
- const char *e;
- int r;
-
- /* Try via kdbus first, and then directly */
-
- assert(_bus);
-
-#ifdef ENABLE_KDBUS
- r = sd_bus_new(&bus);
- if (r < 0)
- return r;
-
- if (asprintf(&bus->address, KERNEL_USER_BUS_FMT, (unsigned long) getuid()) < 0)
- return -ENOMEM;
-
- bus->bus_client = true;
-
- r = sd_bus_start(bus);
- if (r >= 0) {
- *_bus = bus;
- bus = NULL;
- return 0;
- }
-
- bus = sd_bus_unref(bus);
-#endif
-
- e = secure_getenv("XDG_RUNTIME_DIR");
- if (!e)
- return sd_bus_open_user(_bus);
-
- ee = bus_address_escape(e);
- if (!ee)
- return -ENOMEM;
-
- r = sd_bus_new(&bus);
- if (r < 0)
- return r;
-
- bus->address = strjoin("unix:path=", ee, "/systemd/private", NULL);
- if (!bus->address)
- return -ENOMEM;
-
- r = sd_bus_start(bus);
- if (r < 0)
- return sd_bus_open_user(_bus);
-
- r = bus_check_peercred(bus);
- if (r < 0)
- return r;
-
- *_bus = bus;
- bus = NULL;
-
- return 0;
-}
-
-int bus_print_property(const char *name, sd_bus_message *property, bool all) {
- char type;
- const char *contents;
- int r;
-
- assert(name);
- assert(property);
-
- r = sd_bus_message_peek_type(property, &type, &contents);
- if (r < 0)
- return r;
-
- switch (type) {
-
- case SD_BUS_TYPE_STRING: {
- const char *s;
-
- r = sd_bus_message_read_basic(property, type, &s);
- if (r < 0)
- return r;
-
- if (all || !isempty(s))
- printf("%s=%s\n", name, s);
-
- return 1;
- }
-
- case SD_BUS_TYPE_BOOLEAN: {
- bool b;
-
- r = sd_bus_message_read_basic(property, type, &b);
- if (r < 0)
- return r;
-
- printf("%s=%s\n", name, yes_no(b));
-
- return 1;
- }
-
- case SD_BUS_TYPE_UINT64: {
- uint64_t u;
-
- r = sd_bus_message_read_basic(property, type, &u);
- if (r < 0)
- return r;
-
- /* Yes, heuristics! But we can change this check
- * should it turn out to not be sufficient */
-
- if (endswith(name, "Timestamp")) {
- char timestamp[FORMAT_TIMESTAMP_MAX], *t;
-
- t = format_timestamp(timestamp, sizeof(timestamp), u);
- if (t || all)
- printf("%s=%s\n", name, strempty(t));
-
- } else if (strstr(name, "USec")) {
- char timespan[FORMAT_TIMESPAN_MAX];
-
- printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
- } else
- printf("%s=%llu\n", name, (unsigned long long) u);
-
- return 1;
- }
-
- case SD_BUS_TYPE_UINT32: {
- uint32_t u;
-
- r = sd_bus_message_read_basic(property, type, &u);
- if (r < 0)
- return r;
-
- if (strstr(name, "UMask") || strstr(name, "Mode"))
- printf("%s=%04o\n", name, u);
- else
- printf("%s=%u\n", name, (unsigned) u);
-
- return 1;
- }
-
- case SD_BUS_TYPE_INT32: {
- int32_t i;
-
- r = sd_bus_message_read_basic(property, type, &i);
- if (r < 0)
- return r;
-
- printf("%s=%i\n", name, (int) i);
- return 1;
- }
-
- case SD_BUS_TYPE_DOUBLE: {
- double d;
-
- r = sd_bus_message_read_basic(property, type, &d);
- if (r < 0)
- return r;
-
- printf("%s=%g\n", name, d);
- return 1;
- }
-
- case SD_BUS_TYPE_ARRAY:
- if (streq(contents, "s")) {
- bool first = true;
- const char *str;
-
- r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
- if (r < 0)
- return r;
-
- while((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
- if (first)
- printf("%s=", name);
-
- printf("%s%s", first ? "" : " ", str);
-
- first = false;
- }
- if (r < 0)
- return r;
-
- if (first && all)
- printf("%s=", name);
- if (!first || all)
- puts("");
-
- r = sd_bus_message_exit_container(property);
- if (r < 0)
- return r;
-
- return 1;
-
- } else if (streq(contents, "y")) {
- const uint8_t *u;
- size_t n;
-
- r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
- if (r < 0)
- return r;
-
- if (all || n > 0) {
- unsigned int i;
-
- printf("%s=", name);
-
- for (i = 0; i < n; i++)
- printf("%02x", u[i]);
-
- puts("");
- }
-
- return 1;
-
- } else if (streq(contents, "u")) {
- uint32_t *u;
- size_t n;
-
- r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
- if (r < 0)
- return r;
-
- if (all || n > 0) {
- unsigned int i;
-
- printf("%s=", name);
-
- for (i = 0; i < n; i++)
- printf("%08x", u[i]);
-
- puts("");
- }
-
- return 1;
- }
-
- break;
- }
-
- return 0;
-}
-
-int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
-
- assert(bus);
- assert(path);
-
- r = sd_bus_call_method(bus,
- dest,
- path,
- "org.freedesktop.DBus.Properties",
- "GetAll",
- &error,
- &reply,
- "s", "");
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
- if (r < 0)
- return r;
-
- while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
- const char *name;
- const char *contents;
-
- r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
- if (r < 0)
- return r;
-
- if (!filter || strv_find(filter, name)) {
- r = sd_bus_message_peek_type(reply, NULL, &contents);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
- if (r < 0)
- return r;
-
- r = bus_print_property(name, reply, all);
- if (r < 0)
- return r;
- if (r == 0) {
- if (all)
- printf("%s=[unprintable]\n", name);
- /* skip what we didn't read */
- r = sd_bus_message_skip(reply, contents);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return r;
- } else {
- r = sd_bus_message_skip(reply, "v");
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return r;
- }
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
- sd_id128_t *p = userdata;
- const void *v;
- size_t n;
- int r;
-
- r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
- if (r < 0)
- return r;
-
- if (n == 0)
- *p = SD_ID128_NULL;
- else if (n == 16)
- memcpy((*p).bytes, v, n);
- else
- return -EINVAL;
-
- return 0;
-}
-
-static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
- char type;
- int r;
-
- r = sd_bus_message_peek_type(m, &type, NULL);
- if (r < 0)
- return r;
-
- switch (type) {
- case SD_BUS_TYPE_STRING: {
- const char *s;
- char *str;
- char **p = userdata;
-
- r = sd_bus_message_read_basic(m, type, &s);
- if (r < 0)
- break;
-
- if (isempty(s))
- break;
-
- str = strdup(s);
- if (!str) {
- r = -ENOMEM;
- break;
- }
- free(*p);
- *p = str;
-
- break;
- }
-
- case SD_BUS_TYPE_ARRAY: {
- _cleanup_strv_free_ char **l = NULL;
- char ***p = userdata;
-
- r = bus_message_read_strv_extend(m, &l);
- if (r < 0)
- break;
-
- strv_free(*p);
- *p = l;
- l = NULL;
-
- break;
- }
-
- case SD_BUS_TYPE_BOOLEAN: {
- unsigned b;
- bool *p = userdata;
-
- r = sd_bus_message_read_basic(m, type, &b);
- if (r < 0)
- break;
-
- *p = b;
-
- break;
- }
-
- case SD_BUS_TYPE_UINT32: {
- uint64_t u;
- uint32_t *p = userdata;
-
- r = sd_bus_message_read_basic(m, type, &u);
- if (r < 0)
- break;
-
- *p = u;
-
- break;
- }
-
- case SD_BUS_TYPE_UINT64: {
- uint64_t t;
- uint64_t *p = userdata;
-
- r = sd_bus_message_read_basic(m, type, &t);
- if (r < 0)
- break;
-
- *p = t;
-
- break;
- }
-
- default:
- break;
- }
-
- return r;
-}
-
-int bus_map_all_properties(sd_bus *bus,
- const char *destination,
- const char *path,
- const struct bus_properties_map *map,
- void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
-
- assert(bus);
- assert(destination);
- assert(path);
- assert(map);
-
- r = sd_bus_call_method(
- bus,
- destination,
- path,
- "org.freedesktop.DBus.Properties",
- "GetAll",
- &error,
- &m,
- "s", "");
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
- if (r < 0)
- return r;
-
- while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
- const struct bus_properties_map *prop;
- const char *member;
- const char *contents;
- void *v;
- unsigned i;
-
- r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
- if (r < 0)
- return r;
-
- for (i = 0, prop = NULL; map[i].member; i++)
- if (streq(map[i].member, member)) {
- prop = &map[i];
- break;
- }
-
- if (prop) {
- r = sd_bus_message_peek_type(m, NULL, &contents);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
- if (r < 0)
- return r;
-
- v = (uint8_t *)userdata + prop->offset;
- if (map[i].set)
- r = prop->set(bus, member, m, &error, v);
- else
- r = map_basic(bus, member, m, &error, v);
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
- } else {
- r = sd_bus_message_skip(m, "v");
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
- }
-
- return r;
-}
-
-int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
- int r;
-
- assert(transport >= 0);
- assert(transport < _BUS_TRANSPORT_MAX);
- assert(bus);
-
- assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
- assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
-
- switch (transport) {
-
- case BUS_TRANSPORT_LOCAL:
- if (user)
- r = sd_bus_default_user(bus);
- else
- r = sd_bus_default_system(bus);
-
- break;
-
- case BUS_TRANSPORT_REMOTE:
- r = sd_bus_open_system_remote(host, bus);
- break;
-
- case BUS_TRANSPORT_CONTAINER:
- r = sd_bus_open_system_container(host, bus);
- break;
-
- default:
- assert_not_reached("Hmm, unknown transport type.");
- }
-
- return r;
-}
-
-int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
- int r;
-
- assert(transport >= 0);
- assert(transport < _BUS_TRANSPORT_MAX);
- assert(bus);
-
- assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
- assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
-
- switch (transport) {
-
- case BUS_TRANSPORT_LOCAL:
- if (user)
- r = bus_open_user_systemd(bus);
- else
- r = bus_open_system_systemd(bus);
-
- break;
-
- case BUS_TRANSPORT_REMOTE:
- r = sd_bus_open_system_remote(host, bus);
- break;
-
- case BUS_TRANSPORT_CONTAINER:
- r = sd_bus_open_system_container(host, bus);
- break;
-
- default:
- assert_not_reached("Hmm, unknown transport type.");
- }
-
- return r;
-}
-
-int bus_property_get_tristate(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- int *tristate = userdata;
-
- return sd_bus_message_append(reply, "b", *tristate > 0);
-}
-
-int bus_property_get_bool(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- int b = *(bool*) userdata;
-
- return sd_bus_message_append_basic(reply, 'b', &b);
-}
-
-#if __SIZEOF_SIZE_T__ != 8
-int bus_property_get_size(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- uint64_t sz = *(size_t*) userdata;
-
- return sd_bus_message_append_basic(reply, 't', &sz);
-}
-#endif
-
-#if __SIZEOF_LONG__ != 8
-int bus_property_get_long(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- int64_t l = *(long*) userdata;
-
- return sd_bus_message_append_basic(reply, 'x', &l);
-}
-
-int bus_property_get_ulong(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- uint64_t ul = *(unsigned long*) userdata;
-
- return sd_bus_message_append_basic(reply, 't', &ul);
-}
-#endif
-
-int bus_log_parse_error(int r) {
- log_error("Failed to parse message: %s", strerror(-r));
- return r;
-}
-
-int bus_log_create_error(int r) {
- log_error("Failed to create message: %s", strerror(-r));
- return r;
-}
-
-int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
- assert(message);
- assert(u);
-
- return sd_bus_message_read(
- message,
- "(ssssssouso)",
- &u->id,
- &u->description,
- &u->load_state,
- &u->active_state,
- &u->sub_state,
- &u->following,
- &u->unit_path,
- &u->job_id,
- &u->job_type,
- &u->job_path);
-}
-
-int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
- assert(m);
-
- if (r < 0) {
- if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
- sd_bus_reply_method_errno(m, r, error);
-
- } else if (sd_bus_error_is_set(error)) {
- if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
- sd_bus_reply_method_error(m, error);
- } else
- return r;
-
- log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s",
- bus_message_type_to_string(m->header->type),
- strna(m->sender),
- strna(m->path),
- strna(m->interface),
- strna(m->member),
- strna(m->root_container.signature),
- bus_error_message(error, r));
-
- return 1;
-}
diff --git a/src/libsystemd/bus-util.h b/src/libsystemd/bus-util.h
deleted file mode 100644
index 51e1613..0000000
--- a/src/libsystemd/bus-util.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "sd-event.h"
-#include "sd-bus.h"
-#include "hashmap.h"
-#include "time-util.h"
-#include "util.h"
-
-typedef enum BusTransport {
- BUS_TRANSPORT_LOCAL,
- BUS_TRANSPORT_REMOTE,
- BUS_TRANSPORT_CONTAINER,
- _BUS_TRANSPORT_MAX,
- _BUS_TRANSPORT_INVALID = -1
-} BusTransport;
-
-typedef int (*bus_property_set_t) (sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
-
-struct bus_properties_map {
- const char *member;
- const char *signature;
- bus_property_set_t set;
- size_t offset;
-};
-
-int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
-
-int bus_map_all_properties(sd_bus *bus,
- const char *destination,
- const char *path,
- const struct bus_properties_map *map,
- void *userdata);
-
-int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name);
-
-typedef bool (*check_idle_t)(void *userdata);
-
-int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout, check_idle_t check_idle, void *userdata);
-
-int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error);
-
-int bus_check_peercred(sd_bus *c);
-
-int bus_verify_polkit(sd_bus *bus, sd_bus_message *m, const char *action, bool interactive, bool *_challenge, sd_bus_error *e);
-
-int bus_verify_polkit_async(sd_bus *bus, Hashmap **registry, sd_bus_message *m, const char *action, bool interactive, sd_bus_error *error, sd_bus_message_handler_t callback, void *userdata);
-void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry);
-
-int bus_open_system_systemd(sd_bus **_bus);
-int bus_open_user_systemd(sd_bus **_bus);
-
-int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus);
-int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
-
-int bus_print_property(const char *name, sd_bus_message *property, bool all);
-int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all);
-
-int bus_property_get_tristate(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
-int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
-
-#define bus_property_get_usec ((sd_bus_property_get_t) NULL)
-#define bus_property_set_usec ((sd_bus_property_set_t) NULL)
-
-assert_cc(sizeof(int) == sizeof(int32_t));
-#define bus_property_get_int ((sd_bus_property_get_t) NULL)
-
-assert_cc(sizeof(unsigned) == sizeof(unsigned));
-#define bus_property_get_unsigned ((sd_bus_property_get_t) NULL)
-
-/* On 64bit machines we can use the default serializer for size_t and
- * friends, otherwise we need to cast this manually */
-#if __SIZEOF_SIZE_T__ == 8
-#define bus_property_get_size ((sd_bus_property_get_t) NULL)
-#else
-int bus_property_get_size(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
-#endif
-
-#if __SIZEOF_LONG__ == 8
-#define bus_property_get_long ((sd_bus_property_get_t) NULL)
-#define bus_property_get_ulong ((sd_bus_property_get_t) NULL)
-#else
-int bus_property_get_long(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
-int bus_property_get_ulong(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
-#endif
-
-/* uid_t and friends on Linux 32 bit. This means we can just use the
- * default serializer for 32bit unsigned, for serializing it, and map
- * it to NULL here */
-assert_cc(sizeof(uid_t) == sizeof(uint32_t));
-#define bus_property_get_uid ((sd_bus_property_get_t) NULL)
-
-assert_cc(sizeof(gid_t) == sizeof(uint32_t));
-#define bus_property_get_gid ((sd_bus_property_get_t) NULL)
-
-assert_cc(sizeof(pid_t) == sizeof(uint32_t));
-#define bus_property_get_pid ((sd_bus_property_get_t) NULL)
-
-assert_cc(sizeof(mode_t) == sizeof(uint32_t));
-#define bus_property_get_mode ((sd_bus_property_get_t) NULL)
-
-int bus_log_parse_error(int r);
-int bus_log_create_error(int r);
-
-typedef struct UnitInfo {
- const char *id;
- const char *description;
- const char *load_state;
- const char *active_state;
- const char *sub_state;
- const char *following;
- const char *unit_path;
- uint32_t job_id;
- const char *job_type;
- const char *job_path;
-} UnitInfo;
-
-int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus*, sd_bus_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_message*, sd_bus_message_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_creds*, sd_bus_creds_unref);
-
-#define _cleanup_bus_unref_ _cleanup_(sd_bus_unrefp)
-#define _cleanup_bus_message_unref_ _cleanup_(sd_bus_message_unrefp)
-#define _cleanup_bus_creds_unref_ _cleanup_(sd_bus_creds_unrefp)
-#define _cleanup_bus_error_free_ _cleanup_(sd_bus_error_free)
-
-#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \
- int function(sd_bus *bus, \
- const char *path, \
- const char *interface, \
- const char *property, \
- sd_bus_message *reply, \
- void *userdata, \
- sd_bus_error *error) { \
- \
- const char *value; \
- type *field = userdata; \
- int r; \
- \
- assert(bus); \
- assert(reply); \
- assert(field); \
- \
- value = strempty(name##_to_string(*field)); \
- \
- r = sd_bus_message_append_basic(reply, 's', value); \
- if (r < 0) \
- return r; \
- \
- return 1; \
- } \
- struct __useless_struct_to_allow_trailing_semicolon__
-
-#define BUS_PROPERTY_DUAL_TIMESTAMP(name, offset, flags) \
- SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \
- SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags))
-
-int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);
diff --git a/src/libsystemd/busctl.c b/src/libsystemd/busctl.c
deleted file mode 100644
index e962ba3..0000000
--- a/src/libsystemd/busctl.c
+++ /dev/null
@@ -1,514 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <getopt.h>
-
-#include "strv.h"
-#include "util.h"
-#include "log.h"
-#include "build.h"
-#include "pager.h"
-
-#include "sd-bus.h"
-#include "bus-message.h"
-#include "bus-internal.h"
-#include "bus-util.h"
-#include "bus-dump.h"
-
-static bool arg_no_pager = false;
-static char *arg_address = NULL;
-static bool arg_unique = false;
-static bool arg_acquired = false;
-static bool arg_activatable = false;
-static bool arg_show_machine = false;
-static char **arg_matches = NULL;
-static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
-static char *arg_host = NULL;
-static bool arg_user = false;
-
-static void pager_open_if_enabled(void) {
-
- /* Cache result before we open the pager */
- if (arg_no_pager)
- return;
-
- pager_open(false);
-}
-
-static int list_bus_names(sd_bus *bus, char **argv) {
- _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
- _cleanup_free_ char **merged = NULL;
- _cleanup_hashmap_free_ Hashmap *names = NULL;
- char **i;
- int r;
- size_t max_i = 0;
- unsigned n = 0;
- void *v;
- char *k;
- Iterator iterator;
-
- assert(bus);
-
- r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
- if (r < 0) {
- log_error("Failed to list names: %s", strerror(-r));
- return r;
- }
-
- pager_open_if_enabled();
-
- names = hashmap_new(string_hash_func, string_compare_func);
- if (!names)
- return log_oom();
-
- STRV_FOREACH(i, acquired) {
- max_i = MAX(max_i, strlen(*i));
-
- r = hashmap_put(names, *i, INT_TO_PTR(1));
- if (r < 0) {
- log_error("Failed to add to hashmap: %s", strerror(-r));
- return r;
- }
- }
-
- STRV_FOREACH(i, activatable) {
- max_i = MAX(max_i, strlen(*i));
-
- r = hashmap_put(names, *i, INT_TO_PTR(2));
- if (r < 0 && r != -EEXIST) {
- log_error("Failed to add to hashmap: %s", strerror(-r));
- return r;
- }
- }
-
- merged = new(char*, hashmap_size(names) + 1);
- HASHMAP_FOREACH_KEY(v, k, names, iterator)
- merged[n++] = k;
-
- merged[n] = NULL;
- strv_sort(merged);
-
- printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s",
- (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION");
-
- if (arg_show_machine)
- puts(" MACHINE");
- else
- putchar('\n');
-
- STRV_FOREACH(i, merged) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
- sd_id128_t mid;
-
- if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
- /* Activatable */
-
- printf("%-*s", (int) max_i, *i);
- printf(" - - - (activatable) - - ");
- if (arg_show_machine)
- puts(" -");
- else
- putchar('\n');
- continue;
-
- }
-
- if (!arg_unique && (*i)[0] == ':')
- continue;
-
- if (!arg_acquired && (*i)[0] != ':')
- continue;
-
- printf("%-*s", (int) max_i, *i);
-
- r = sd_bus_get_owner(bus, *i, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION, &creds);
- if (r >= 0) {
- const char *unique, *session, *unit;
- pid_t pid;
- uid_t uid;
-
- r = sd_bus_creds_get_pid(creds, &pid);
- if (r >= 0) {
- const char *comm = NULL;
-
- sd_bus_creds_get_comm(creds, &comm);
-
- printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
- } else
- fputs(" - - ", stdout);
-
- r = sd_bus_creds_get_uid(creds, &uid);
- if (r >= 0) {
- _cleanup_free_ char *u = NULL;
-
- u = uid_to_name(uid);
- if (!u)
- return log_oom();
-
- if (strlen(u) > 16)
- u[16] = 0;
-
- printf(" %-16s", u);
- } else
- fputs(" - ", stdout);
-
- r = sd_bus_creds_get_unique_name(creds, &unique);
- if (r >= 0)
- printf(" %-13s", unique);
- else
- fputs(" - ", stdout);
-
- r = sd_bus_creds_get_unit(creds, &unit);
- if (r >= 0) {
- _cleanup_free_ char *e;
-
- e = ellipsize(unit, 25, 100);
- if (!e)
- return log_oom();
-
- printf(" %-25s", e);
- } else
- fputs(" - ", stdout);
-
- r = sd_bus_creds_get_session(creds, &session);
- if (r >= 0)
- printf(" %-10s", session);
- else
- fputs(" - ", stdout);
-
- } else
- printf(" - - - - - - ");
-
- if (arg_show_machine) {
- r = sd_bus_get_owner_machine_id(bus, *i, &mid);
- if (r >= 0) {
- char m[SD_ID128_STRING_MAX];
- printf(" %s\n", sd_id128_to_string(mid, m));
- } else
- puts(" -");
- } else
- putchar('\n');
- }
-
- return 0;
-}
-
-static int monitor(sd_bus *bus, char *argv[]) {
- bool added_something = false;
- char **i;
- int r;
-
- STRV_FOREACH(i, argv+1) {
- _cleanup_free_ char *m = NULL;
-
- if (!service_name_is_valid(*i)) {
- log_error("Invalid service name '%s'", *i);
- return -EINVAL;
- }
-
- m = strjoin("sender='", *i, "'", NULL);
- if (!m)
- return log_oom();
-
- r = sd_bus_add_match(bus, m, NULL, NULL);
- if (r < 0) {
- log_error("Failed to add match: %s", strerror(-r));
- return r;
- }
-
- added_something = true;
- }
-
- STRV_FOREACH(i, arg_matches) {
- r = sd_bus_add_match(bus, *i, NULL, NULL);
- if (r < 0) {
- log_error("Failed to add match: %s", strerror(-r));
- return r;
- }
-
- added_something = true;
- }
-
- if (!added_something) {
- r = sd_bus_add_match(bus, "", NULL, NULL);
- if (r < 0) {
- log_error("Failed to add match: %s", strerror(-r));
- return r;
- }
- }
-
- for (;;) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-
- r = sd_bus_process(bus, &m);
- if (r < 0) {
- log_error("Failed to process bus: %s", strerror(-r));
- return r;
- }
-
- if (m) {
- bus_message_dump(m, stdout, true);
- continue;
- }
-
- if (r > 0)
- continue;
-
- r = sd_bus_wait(bus, (uint64_t) -1);
- if (r < 0) {
- log_error("Failed to wait for bus: %s", strerror(-r));
- return r;
- }
- }
-}
-
-static int status(sd_bus *bus, char *argv[]) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
- pid_t pid;
- int r;
-
- assert(bus);
-
- if (strv_length(argv) != 2) {
- log_error("Expects one argument.");
- return -EINVAL;
- }
-
- r = parse_pid(argv[1], &pid);
- if (r < 0)
- r = sd_bus_get_owner(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
- else
- r = sd_bus_creds_new_from_pid(pid, _SD_BUS_CREDS_ALL, &creds);
-
- if (r < 0) {
- log_error("Failed to get credentials: %s", strerror(-r));
- return r;
- }
-
- bus_creds_dump(creds, NULL);
- return 0;
-}
-
-static int help(void) {
-
- printf("%s [OPTIONS...] {COMMAND} ...\n\n"
- "Introspect the bus.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --no-pager Do not pipe output into a pager\n"
- " --system Connect to system bus\n"
- " --user Connect to user bus\n"
- " -H --host=[USER@]HOST Operate on remote host\n"
- " -M --machine=CONTAINER Operate on local container\n"
- " --address=ADDRESS Connect to bus specified by address\n"
- " --show-machine Show machine ID column in list\n"
- " --unique Only show unique names\n"
- " --acquired Only show acquired names\n"
- " --activatable Only show activatable names\n"
- " --match=MATCH Only show matching messages\n\n"
- "Commands:\n"
- " list List bus names\n"
- " monitor [SERVICE...] Show bus traffic\n"
- " status NAME Show name status\n"
- " help Show this help\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_VERSION = 0x100,
- ARG_NO_PAGER,
- ARG_SYSTEM,
- ARG_USER,
- ARG_ADDRESS,
- ARG_MATCH,
- ARG_SHOW_MACHINE,
- ARG_UNIQUE,
- ARG_ACQUIRED,
- ARG_ACTIVATABLE
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "no-pager", no_argument, NULL, ARG_NO_PAGER },
- { "system", no_argument, NULL, ARG_SYSTEM },
- { "user", no_argument, NULL, ARG_USER },
- { "address", required_argument, NULL, ARG_ADDRESS },
- { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
- { "unique", no_argument, NULL, ARG_UNIQUE },
- { "acquired", no_argument, NULL, ARG_ACQUIRED },
- { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
- { "match", required_argument, NULL, ARG_MATCH },
- { "host", required_argument, NULL, 'H' },
- { "machine", required_argument, NULL, 'M' },
- {},
- };
-
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
-
- switch (c) {
-
- case 'h':
- return help();
-
- case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
-
- case ARG_NO_PAGER:
- arg_no_pager = true;
- break;
-
- case ARG_USER:
- arg_user = true;
- break;
-
- case ARG_SYSTEM:
- arg_user = false;
- break;
-
- case ARG_ADDRESS:
- arg_address = optarg;
- break;
-
- case ARG_SHOW_MACHINE:
- arg_show_machine = true;
- break;
-
- case ARG_UNIQUE:
- arg_unique = true;
- break;
-
- case ARG_ACQUIRED:
- arg_acquired = true;
- break;
-
- case ARG_ACTIVATABLE:
- arg_activatable = true;
- break;
-
- case ARG_MATCH:
- if (strv_extend(&arg_matches, optarg) < 0)
- return log_oom();
- break;
-
- case 'H':
- arg_transport = BUS_TRANSPORT_REMOTE;
- arg_host = optarg;
- break;
-
- case 'M':
- arg_transport = BUS_TRANSPORT_CONTAINER;
- arg_host = optarg;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached("Unhandled option");
- }
- }
-
- if (!arg_unique && !arg_acquired && !arg_activatable)
- arg_unique = arg_acquired = arg_activatable = true;
-
- return 1;
-}
-
-static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
- assert(bus);
-
- if (optind >= argc ||
- streq(argv[optind], "list"))
- return list_bus_names(bus, argv + optind);
-
- if (streq(argv[optind], "monitor"))
- return monitor(bus, argv + optind);
-
- if (streq(argv[optind], "status"))
- return status(bus, argv + optind);
-
- if (streq(argv[optind], "help"))
- return help();
-
- log_error("Unknown command '%s'", argv[optind]);
- return -EINVAL;
-}
-
-int main(int argc, char *argv[]) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
- int r;
-
- log_parse_environment();
- log_open();
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- goto finish;
-
- if (arg_address) {
- r = sd_bus_new(&bus);
- if (r < 0) {
- log_error("Failed to allocate bus: %s", strerror(-r));
- goto finish;
- }
-
- r = sd_bus_set_address(bus, arg_address);
- if (r < 0) {
- log_error("Failed to set address: %s", strerror(-r));
- goto finish;
- }
-
- r = sd_bus_set_bus_client(bus, true);
- if (r < 0) {
- log_error("Failed to set bus client: %s", strerror(-r));
- goto finish;
- }
-
- r = sd_bus_start(bus);
- } else
- r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
-
- if (r < 0) {
- log_error("Failed to connect to bus: %s", strerror(-r));
- goto finish;
- }
-
- r = busctl_main(bus, argc, argv);
-
-finish:
- pager_close();
-
- strv_free(arg_matches);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/libsystemd/event-util.h b/src/libsystemd/event-util.h
deleted file mode 100644
index e58020d..0000000
--- a/src/libsystemd/event-util.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event*, sd_event_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, sd_event_source_unref);
-
-#define _cleanup_event_unref_ _cleanup_(sd_event_unrefp)
-#define _cleanup_event_source_unref_ _cleanup_(sd_event_source_unrefp)
diff --git a/src/libsystemd/kdbus.h b/src/libsystemd/kdbus.h
deleted file mode 100644
index fe0bb48..0000000
--- a/src/libsystemd/kdbus.h
+++ /dev/null
@@ -1,900 +0,0 @@
-/*
- * Copyright (C) 2013 Kay Sievers
- * Copyright (C) 2013 Greg Kroah-Hartman <gregkh at linuxfoundation.org>
- * Copyright (C) 2013 Linux Foundation
- * Copyright (C) 2013 Lennart Poettering
- * Copyright (C) 2013 Daniel Mack <daniel at zonque.org>
- *
- * kdbus is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation; either version 2.1 of the License, or (at
- * your option) any later version.
- *
- * "Everything should be made as simple as possible, but not simpler."
- * -- Albert Einstein
- */
-
-#ifndef _KDBUS_H_
-#define _KDBUS_H_
-
-#ifndef __KERNEL__
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <linux/types.h>
-#endif
-
-#define KDBUS_IOC_MAGIC 0x95
-#define KDBUS_SRC_ID_KERNEL (0)
-#define KDBUS_DST_ID_NAME (0)
-#define KDBUS_MATCH_ID_ANY (~0ULL)
-#define KDBUS_DST_ID_BROADCAST (~0ULL)
-
-/**
- * struct kdbus_notify_id_change - name registry change message
- * @id: New or former owner of the name
- * @flags: flags field from KDBUS_HELLO_*
- *
- * Sent from kernel to userspace when the owner or activator of
- * a well-known name changes.
- *
- * Attached to:
- * KDBUS_ITEM_ID_ADD
- * KDBUS_ITEM_ID_REMOVE
- */
-struct kdbus_notify_id_change {
- __u64 id;
- __u64 flags;
-};
-
-/**
- * struct kdbus_notify_name_change - name registry change message
- * @old: ID and flags of former owner of a name
- * @new: ID and flags of new owner of a name
- * @name: Well-known name
- *
- * Sent from kernel to userspace when the owner or activator of
- * a well-known name changes.
- *
- * Attached to:
- * KDBUS_ITEM_NAME_ADD
- * KDBUS_ITEM_NAME_REMOVE
- * KDBUS_ITEM_NAME_CHANGE
- */
-struct kdbus_notify_name_change {
- struct kdbus_notify_id_change old;
- struct kdbus_notify_id_change new;
- char name[0];
-};
-
-/**
- * struct kdbus_creds - process credentials
- * @uid: User ID
- * @gid: Group ID
- * @pid: Process ID
- * @tid: Thread ID
- * @starttime: Starttime of the process
- *
- * The starttime of the process PID. This is useful to detect PID overruns
- * from the client side. i.e. if you use the PID to look something up in
- * /proc/$PID/ you can afterwards check the starttime field of it, to ensure
- * you didn't run into a PID overrun.
- *
- * Attached to:
- * KDBUS_ITEM_CREDS
- */
-struct kdbus_creds {
- __u64 uid;
- __u64 gid;
- __u64 pid;
- __u64 tid;
- __u64 starttime;
-};
-
-/**
- * struct kdbus_audit - audit information
- * @sessionid: The audit session ID
- * @loginuid: The audit login uid
- *
- * Attached to:
- * KDBUS_ITEM_AUDIT
- */
-struct kdbus_audit {
- __u64 sessionid;
- __u64 loginuid;
-};
-
-/**
- * struct kdbus_timestamp
- * @seqnum: Global per-namespace message sequence number
- * @monotonic_ns: Monotonic timestamp, in nanoseconds
- * @realtime_ns: Realtime timestamp, in nanoseconds
- *
- * Attached to:
- * KDBUS_ITEM_TIMESTAMP
- */
-struct kdbus_timestamp {
- __u64 seqnum;
- __u64 monotonic_ns;
- __u64 realtime_ns;
-};
-
-/**
- * struct kdbus_vec - I/O vector for kdbus payload items
- * @size: The size of the vector
- * @address: Memory address for memory addresses
- * @offset: Offset in the in-message payload memory,
- * relative to the message head
- *
- * Attached to:
- * KDBUS_ITEM_PAYLOAD_VEC
- */
-struct kdbus_vec {
- __u64 size;
- union {
- __u64 address;
- __u64 offset;
- };
-};
-
-/**
- * struct kdbus_memfd - a kdbus memfd
- * @size: The memfd's size
- * @fd: The file descriptor number
- * @__pad: Padding to ensure proper alignement and size
- *
- * Attached to:
- * KDBUS_ITEM_PAYLOAD_MEMFD
- */
-struct kdbus_memfd {
- __u64 size;
- int fd;
- __u32 __pad;
-};
-
-/**
- * struct kdbus_name - a registered well-known name with its flags
- * @flags: flags from KDBUS_NAME_*
- * @name: well-known name
- *
- * Attached to:
- * KDBUS_ITEM_NAME
- */
-struct kdbus_name {
- __u64 flags;
- char name[0];
-};
-
-/**
- * struct kdbus_policy_access - policy access item
- * @type: One of KDBUS_POLICY_ACCESS_* types
- * @bits: Access to grant. One of KDBUS_POLICY_*
- * @id: For KDBUS_POLICY_ACCESS_USER, the uid
- * For KDBUS_POLICY_ACCESS_GROUP, the gid
- *
- * Embedded in:
- * struct kdbus_policy
- */
-struct kdbus_policy_access {
- __u64 type; /* USER, GROUP, WORLD */
- __u64 bits; /* RECV, SEND, OWN */
- __u64 id; /* uid, gid, 0 */
-};
-
-/**
- * struct kdbus_policy - a policy item
- * @access: Policy access details
- * @name: Well-known name to grant access to
- *
- * Attached to:
- * KDBUS_POLICY_ACCESS
- * KDBUS_ITEM_POLICY_NAME
- */
-struct kdbus_policy {
- union {
- struct kdbus_policy_access access;
- char name[0];
- };
-};
-
-/**
- * enum kdbus_item_type - item types to chain data in a list
- * @_KDBUS_ITEM_NULL: Uninitialized/invalid
- * @_KDBUS_ITEM_USER_BASE: Start of user items
- * @KDBUS_ITEM_PAYLOAD_VEC: Vector to data
- * @KDBUS_ITEM_PAYLOAD_OFF: Data at returned offset to message head
- * @KDBUS_ITEM_PAYLOAD_MEMFD: Data as sealed memfd
- * @KDBUS_ITEM_FDS: Attached file descriptors
- * @KDBUS_ITEM_BLOOM: For broadcasts, carries bloom filter
- * @KDBUS_ITEM_BLOOM_SIZE: Desired bloom size, used by KDBUS_CMD_BUS_MAKE
- * @KDBUS_ITEM_DST_NAME: Destination's well-known name
- * @KDBUS_ITEM_MAKE_NAME: Name of namespace, bus, endpoint
- * @KDBUS_ITEM_MEMFD_NAME: The human readable name of a memfd (debugging)
- * @_KDBUS_ITEM_POLICY_BASE: Start of policy items
- * @KDBUS_ITEM_POLICY_NAME: Policy in struct kdbus_policy
- * @KDBUS_ITEM_POLICY_ACCESS: Policy in struct kdbus_policy
- * @_KDBUS_ITEM_ATTACH_BASE: Start of metadata attach items
- * @KDBUS_ITEM_NAME: Well-know name with flags
- * @KDBUS_ITEM_ID: Connection ID
- * @KDBUS_ITEM_TIMESTAMP: Timestamp
- * @KDBUS_ITEM_CREDS: Process credential
- * @KDBUS_ITEM_PID_COMM: Process ID "comm" identifier
- * @KDBUS_ITEM_TID_COMM: Thread ID "comm" identifier
- * @KDBUS_ITEM_EXE: The path of the executable
- * @KDBUS_ITEM_CMDLINE: The process command line
- * @KDBUS_ITEM_CGROUP: The croup membership
- * @KDBUS_ITEM_CAPS: The process capabilities
- * @KDBUS_ITEM_SECLABEL: The security label
- * @KDBUS_ITEM_AUDIT: The audit IDs
- * @KDBUS_ITEM_CONN_NAME: The connection's human-readable name (debugging)
- * @_KDBUS_ITEM_KERNEL_BASE: Start of kernel-generated message items
- * @KDBUS_ITEM_NAME_ADD: Notify in struct kdbus_notify_name_change
- * @KDBUS_ITEM_NAME_REMOVE: Notify in struct kdbus_notify_name_change
- * @KDBUS_ITEM_NAME_CHANGE: Notify in struct kdbus_notify_name_change
- * @KDBUS_ITEM_ID_ADD: Notify in struct kdbus_notify_id_change
- * @KDBUS_ITEM_ID_REMOVE: Notify in struct kdbus_notify_id_change
- * @KDBUS_ITEM_REPLY_TIMEOUT: Timeout has been reached
- * @KDBUS_ITEM_REPLY_DEAD: Destination died
- */
-enum kdbus_item_type {
- _KDBUS_ITEM_NULL,
- _KDBUS_ITEM_USER_BASE,
- KDBUS_ITEM_PAYLOAD_VEC = _KDBUS_ITEM_USER_BASE,
- KDBUS_ITEM_PAYLOAD_OFF,
- KDBUS_ITEM_PAYLOAD_MEMFD,
- KDBUS_ITEM_FDS,
- KDBUS_ITEM_BLOOM,
- KDBUS_ITEM_BLOOM_SIZE,
- KDBUS_ITEM_DST_NAME,
- KDBUS_ITEM_MAKE_NAME,
- KDBUS_ITEM_MEMFD_NAME,
-
- _KDBUS_ITEM_POLICY_BASE = 0x1000,
- KDBUS_ITEM_POLICY_NAME = _KDBUS_ITEM_POLICY_BASE,
- KDBUS_ITEM_POLICY_ACCESS,
-
- _KDBUS_ITEM_ATTACH_BASE = 0x2000,
- KDBUS_ITEM_NAME = _KDBUS_ITEM_ATTACH_BASE,
- KDBUS_ITEM_ID,
- KDBUS_ITEM_TIMESTAMP,
- KDBUS_ITEM_CREDS,
- KDBUS_ITEM_PID_COMM,
- KDBUS_ITEM_TID_COMM,
- KDBUS_ITEM_EXE,
- KDBUS_ITEM_CMDLINE,
- KDBUS_ITEM_CGROUP,
- KDBUS_ITEM_CAPS,
- KDBUS_ITEM_SECLABEL,
- KDBUS_ITEM_AUDIT,
- KDBUS_ITEM_CONN_NAME,
-
- _KDBUS_ITEM_KERNEL_BASE = 0x3000,
- KDBUS_ITEM_NAME_ADD = _KDBUS_ITEM_KERNEL_BASE,
- KDBUS_ITEM_NAME_REMOVE,
- KDBUS_ITEM_NAME_CHANGE,
- KDBUS_ITEM_ID_ADD,
- KDBUS_ITEM_ID_REMOVE,
- KDBUS_ITEM_REPLY_TIMEOUT,
- KDBUS_ITEM_REPLY_DEAD,
-};
-
-/**
- * struct kdbus_item - chain of data blocks
- * @size: Overall data record size
- * @type: Kdbus_item type of data
- * @data: Generic bytes
- * @data32: Generic 32 bit array
- * @data64: Generic 64 bit array
- * @str: Generic string
- * @id: Connection ID
- * @vec: KDBUS_ITEM_PAYLOAD_VEC
- * @creds: KDBUS_ITEM_CREDS
- * @audit: KDBUS_ITEM_AUDIT
- * @timestamp: KDBUS_ITEM_TIMESTAMP
- * @name: KDBUS_ITEM_NAME
- * @memfd: KDBUS_ITEM_PAYLOAD_MEMFD
- * @name_change: KDBUS_ITEM_NAME_ADD
- * KDBUS_ITEM_NAME_REMOVE
- * KDBUS_ITEM_NAME_CHANGE
- * @id_change: KDBUS_ITEM_ID_ADD
- * KDBUS_ITEM_ID_REMOVE
- * @policy: KDBUS_ITEM_POLICY_NAME
- * KDBUS_ITEM_POLICY_ACCESS
- */
-struct kdbus_item {
- __u64 size;
- __u64 type;
- union {
- __u8 data[0];
- __u32 data32[0];
- __u64 data64[0];
- char str[0];
-
- __u64 id;
- struct kdbus_vec vec;
- struct kdbus_creds creds;
- struct kdbus_audit audit;
- struct kdbus_timestamp timestamp;
- struct kdbus_name name;
- struct kdbus_memfd memfd;
- int fds[0];
- struct kdbus_notify_name_change name_change;
- struct kdbus_notify_id_change id_change;
- struct kdbus_policy policy;
- };
-};
-
-/**
- * enum kdbus_msg_flags - type of message
- * @KDBUS_MSG_FLAGS_EXPECT_REPLY: Expect a reply message, used for
- * method calls. The userspace-supplied
- * cookie identifies the message and the
- * respective reply carries the cookie
- * in cookie_reply
- * @KDBUS_MSG_FLAGS_SYNC_REPLY: Wait for destination connection to
- * reply to this message. The
- * KDBUS_CMD_MSG_SEND ioctl() will block
- * until the reply is received, and
- * offset_reply in struct kdbus_msg will
- * yield the offset in the sender's pool
- * where the reply can be found.
- * This flag is only valid if
- * @KDBUS_MSG_FLAGS_EXPECT_REPLY is set as
- * well.
- * @KDBUS_MSG_FLAGS_NO_AUTO_START: Do not start a service, if the addressed
- * name is not currently active
- */
-enum kdbus_msg_flags {
- KDBUS_MSG_FLAGS_EXPECT_REPLY = 1 << 0,
- KDBUS_MSG_FLAGS_SYNC_REPLY = 1 << 1,
- KDBUS_MSG_FLAGS_NO_AUTO_START = 1 << 2,
-};
-
-/**
- * enum kdbus_payload_type - type of payload carried by message
- * @KDBUS_PAYLOAD_KERNEL: Kernel-generated simple message
- * @KDBUS_PAYLOAD_DBUS: D-Bus marshalling "DBusDBus"
- */
-enum kdbus_payload_type {
- KDBUS_PAYLOAD_KERNEL,
- KDBUS_PAYLOAD_DBUS = 0x4442757344427573ULL,
-};
-
-/**
- * struct kdbus_msg - the representation of a kdbus message
- * @size: Total size of the message
- * @flags: Message flags (KDBUS_MSG_FLAGS_*)
- * @priority: Message queue priority value
- * @dst_id: 64-bit ID of the destination connection
- * @src_id: 64-bit ID of the source connection
- * @payload_type: Payload type (KDBUS_PAYLOAD_*)
- * @cookie: Userspace-supplied cookie, for the connection
- * to identify its messages
- * @timeout_ns: The time to wait for a message reply from the peer.
- * If there is no reply, a kernel-generated message
- * with an attached KDBUS_ITEM_REPLY_TIMEOUT item
- * is sent to @src_id.
- * @cookie_reply: A reply to the requesting message with the same
- * cookie. The requesting connection can match its
- * request and the reply with this value
- * @offset_reply: If KDBUS_MSG_FLAGS_WAIT_FOR_REPLY, this field will
- * contain the offset in the sender's pool where the
- * reply is stored.
- * @items: A list of kdbus_items containing the message payload
- */
-struct kdbus_msg {
- __u64 size;
- __u64 flags;
- __s64 priority;
- __u64 dst_id;
- __u64 src_id;
- __u64 payload_type;
- __u64 cookie;
- union {
- __u64 timeout_ns;
- __u64 cookie_reply;
- __u64 offset_reply;
- };
- struct kdbus_item items[0];
-} __attribute__((aligned(8)));
-
-/**
- * enum kdbus_recv_flags - flags for de-queuing messages
- * @KDBUS_RECV_PEEK: Return the next queued message without
- * actually de-queuing it, and without installing
- * any file descriptors or other resources. It is
- * usually used to determine the activating
- * connection of a bus name.
- * @KDBUS_RECV_DROP: Drop and free the next queued message and all
- * its resources without actually receiving it.
- * @KDBUS_RECV_USE_PRIORITY: Only de-queue messages with the specified or
- * higher priority (lowest values); if not set,
- * the priority value is ignored.
- */
-enum kdbus_recv_flags {
- KDBUS_RECV_PEEK = 1 << 0,
- KDBUS_RECV_DROP = 1 << 1,
- KDBUS_RECV_USE_PRIORITY = 1 << 2,
-};
-
-/**
- * struct kdbus_cmd_recv - struct to de-queue a buffered message
- * @flags: KDBUS_RECV_* flags
- * @priority: Minimum priority of the messages to de-queue. Lowest
- * values have the highest priority.
- * @offset: Returned offset in the pool where the message is
- * stored. The user must use KDBUS_CMD_FREE to free
- * the allocated memory.
- *
- * This struct is used with the KDBUS_CMD_MSG_RECV ioctl.
- */
-struct kdbus_cmd_recv {
- __u64 flags;
- __s64 priority;
- __u64 offset;
-} __attribute__((aligned(8)));
-
-/**
- * enum kdbus_policy_access_type - permissions of a policy record
- * @_KDBUS_POLICY_ACCESS_NULL: Uninitialized/invalid
- * @KDBUS_POLICY_ACCESS_USER: Grant access to a uid
- * @KDBUS_POLICY_ACCESS_GROUP: Grant access to gid
- * @KDBUS_POLICY_ACCESS_WORLD: World-accessible
- */
-enum kdbus_policy_access_type {
- _KDBUS_POLICY_ACCESS_NULL,
- KDBUS_POLICY_ACCESS_USER,
- KDBUS_POLICY_ACCESS_GROUP,
- KDBUS_POLICY_ACCESS_WORLD,
-};
-
-/**
- * enum kdbus_policy_access_flags - mode flags
- * @KDBUS_POLICY_RECV: Allow receive
- * @KDBUS_POLICY_SEND: Allow send
- * @KDBUS_POLICY_OWN: Allow to own a well-known name
- */
-enum kdbus_policy_type {
- KDBUS_POLICY_RECV = 1 << 2,
- KDBUS_POLICY_SEND = 1 << 1,
- KDBUS_POLICY_OWN = 1 << 0,
-};
-
-/**
- * struct kdbus_cmd_policy - a series of policies to upload
- * @size: The total size of the structure
- * @policies: The policies to upload
- *
- * A KDBUS_POLICY_NAME must always preceeds a KDBUS_POLICY_ACCESS entry.
- * A new KDBUS_POLICY_NAME can be added after KDBUS_POLICY_ACCESS for
- * chaining multiple policies together.
- */
-struct kdbus_cmd_policy {
- __u64 size;
- struct kdbus_item policies[0];
-} __attribute__((aligned(8)));
-
-/**
- * enum kdbus_hello_flags - flags for struct kdbus_cmd_hello
- * @KDBUS_HELLO_ACCEPT_FD: The connection allows the receiving of
- * any passed file descriptors
- * @KDBUS_HELLO_ACTIVATOR: Special-purpose connection which registers
- * a well-know name for a process to be started
- * when traffic arrives
- * @KDBUS_HELLO_MONITOR: Special-purpose connection to monitor
- * bus traffic
- */
-enum kdbus_hello_flags {
- KDBUS_HELLO_ACCEPT_FD = 1 << 0,
- KDBUS_HELLO_ACTIVATOR = 1 << 1,
- KDBUS_HELLO_MONITOR = 1 << 2,
-};
-
-/**
- * enum kdbus_attach_flags - flags for metadata attachments
- * @KDBUS_ATTACH_TIMESTAMP: Timestamp
- * @KDBUS_ATTACH_CREDS: Credentials
- * @KDBUS_ATTACH_NAMES: Well-known names
- * @KDBUS_ATTACH_COMM: The "comm" process identifier
- * @KDBUS_ATTACH_EXE: The path of the executable
- * @KDBUS_ATTACH_CMDLINE: The process command line
- * @KDBUS_ATTACH_CGROUP: The croup membership
- * @KDBUS_ATTACH_CAPS: The process capabilities
- * @KDBUS_ATTACH_SECLABEL: The security label
- * @KDBUS_ATTACH_AUDIT: The audit IDs
- * @KDBUS_ATTACH_CONN_NAME: The human-readable connection name
- */
-enum kdbus_attach_flags {
- KDBUS_ATTACH_TIMESTAMP = 1 << 0,
- KDBUS_ATTACH_CREDS = 1 << 1,
- KDBUS_ATTACH_NAMES = 1 << 2,
- KDBUS_ATTACH_COMM = 1 << 3,
- KDBUS_ATTACH_EXE = 1 << 4,
- KDBUS_ATTACH_CMDLINE = 1 << 5,
- KDBUS_ATTACH_CGROUP = 1 << 6,
- KDBUS_ATTACH_CAPS = 1 << 7,
- KDBUS_ATTACH_SECLABEL = 1 << 8,
- KDBUS_ATTACH_AUDIT = 1 << 9,
- KDBUS_ATTACH_CONN_NAME = 1 << 10,
-};
-
-/**
- * struct kdbus_cmd_hello - struct to say hello to kdbus
- * @size: The total size of the structure
- * @conn_flags: Connection flags (KDBUS_HELLO_*). The kernel will
- * return its capabilities in that field.
- * @attach_flags: Mask of metadata to attach to each message sent
- * (KDBUS_ATTACH_*)
- * @bus_flags: The flags field copied verbatim from the original
- * KDBUS_CMD_BUS_MAKE ioctl. It's intended to be useful
- * to do negotiation of features of the payload that is
- * transferred (kernel â userspace)
- * @id: The ID of this connection (kernel â userspace)
- * @bloom_size: The bloom filter size chosen by the owner
- * (kernel â userspace)
- * @pool_size: Size of the connection's buffer where the received
- * messages are placed
- * @id128: Unique 128-bit ID of the bus (kernel â userspace)
- * @items: A list of items
- *
- * This struct is used with the KDBUS_CMD_HELLO ioctl.
- */
-struct kdbus_cmd_hello {
- __u64 size;
- __u64 conn_flags;
- __u64 attach_flags;
- __u64 bus_flags;
- __u64 id;
- __u64 bloom_size;
- __u64 pool_size;
- __u8 id128[16];
- struct kdbus_item items[0];
-} __attribute__((aligned(8)));
-
-/* Flags for KDBUS_CMD_{BUS,EP,NS}_MAKE */
-enum kdbus_make_flags {
- KDBUS_MAKE_ACCESS_GROUP = 1 << 0,
- KDBUS_MAKE_ACCESS_WORLD = 1 << 1,
- KDBUS_MAKE_POLICY_OPEN = 1 << 2,
-};
-
-/**
- * struct kdbus_cmd_make - struct to make a bus, an endpoint or a namespace
- * @size: The total size of the struct
- * @flags: Properties for the bus/ep/ns to create
- * @items: Items describing details
- *
- * This structure is used with the KDBUS_CMD_BUS_MAKE, KDBUS_CMD_EP_MAKE and
- * KDBUS_CMD_NS_MAKE ioctls.
- */
-struct kdbus_cmd_make {
- __u64 size;
- __u64 flags;
- struct kdbus_item items[0];
-} __attribute__((aligned(8)));
-
-/**
- * enum kdbus_name_flags - properties of a well-known name
- * @KDBUS_NAME_REPLACE_EXISTING: Try to replace name of other connections
- * @KDBUS_NAME_ALLOW_REPLACEMENT: Allow the replacement of the name
- * @KDBUS_NAME_QUEUE: Name should be queued if busy
- * @KDBUS_NAME_IN_QUEUE: Name is queued
- * @KDBUS_NAME_ACTIVATOR: Name is owned by a activator connection
- */
-enum kdbus_name_flags {
- KDBUS_NAME_REPLACE_EXISTING = 1 << 0,
- KDBUS_NAME_ALLOW_REPLACEMENT = 1 << 1,
- KDBUS_NAME_QUEUE = 1 << 2,
- KDBUS_NAME_IN_QUEUE = 1 << 3,
- KDBUS_NAME_ACTIVATOR = 1 << 4,
-};
-
-/**
- * struct kdbus_cmd_name - struct to describe a well-known name
- * @size: The total size of the struct
- * @flags: Flags for a name entry (KDBUS_NAME_*)
- * @owner_id: The current owner of the name. For requests,
- * privileged users may set this field to
- * (de)register names on behalf of other connections.
- * @conn_flags: The flags of the owning connection (KDBUS_HELLO_*)
- * @name: The well-known name
- *
- * This structure is used with the KDBUS_CMD_NAME_ACQUIRE ioctl.
- */
-struct kdbus_cmd_name {
- __u64 size;
- __u64 flags;
- __u64 owner_id;
- __u64 conn_flags;
- char name[0];
-} __attribute__((aligned(8)));
-
-/**
- * enum kdbus_name_list_flags - what to include into the returned list
- * @KDBUS_NAME_LIST_UNIQUE: All active connections
- * @KDBUS_NAME_LIST_NAMES: All known well-known names
- * @KDBUS_NAME_LIST_ACTIVATORS: All activator connections
- * @KDBUS_NAME_LIST_QUEUED: All queued-up names
- */
-enum kdbus_name_list_flags {
- KDBUS_NAME_LIST_UNIQUE = 1 << 0,
- KDBUS_NAME_LIST_NAMES = 1 << 1,
- KDBUS_NAME_LIST_ACTIVATORS = 1 << 2,
- KDBUS_NAME_LIST_QUEUED = 1 << 3,
-};
-
-/**
- * struct kdbus_cmd_name_list - request a list of name entries
- * @flags: Flags for the query (KDBUS_NAME_LIST_*)
- * @offset: The returned offset in the caller's pool buffer.
- * The user must use KDBUS_CMD_FREE to free the
- * allocated memory.
- *
- * This structure is used with the KDBUS_CMD_NAME_LIST ioctl.
- */
-struct kdbus_cmd_name_list {
- __u64 flags;
- __u64 offset;
-} __attribute__((aligned(8)));
-
-/**
- * struct kdbus_name_list - information returned by KDBUS_CMD_NAME_LIST
- * @size: The total size of the structure
- * @names: A list of names
- *
- * Note that the user is responsible for freeing the allocated memory with
- * the KDBUS_CMD_FREE ioctl.
- */
-struct kdbus_name_list {
- __u64 size;
- struct kdbus_cmd_name names[0];
-};
-
-/**
- * struct kdbus_cmd_conn_info - struct used for KDBUS_CMD_CONN_INFO ioctl
- * @size: The total size of the struct
- * @flags: KDBUS_ATTACH_* flags
- * @id: The 64-bit ID of the connection. If set to zero, passing
- * @name is required. kdbus will look up the name to
- * determine the ID in this case.
- * @offset: Returned offset in the caller's pool buffer where the
- * kdbus_conn_info struct result is stored. The user must
- * use KDBUS_CMD_FREE to free the allocated memory.
- * @name: The optional well-known name to look up. Only needed in
- * case @id is zero.
- *
- * On success, the KDBUS_CMD_CONN_INFO ioctl will return 0 and @offset will
- * tell the user the offset in the connection pool buffer at which to find the
- * result in a struct kdbus_conn_info.
- */
-struct kdbus_cmd_conn_info {
- __u64 size;
- __u64 flags;
- __u64 id;
- __u64 offset;
- char name[0];
-} __attribute__((aligned(8)));
-
-/**
- * struct kdbus_conn_info - information returned by KDBUS_CMD_CONN_INFO
- * @size: The total size of the struct
- * @id: The connection's 64-bit ID
- * @flags: The connection's flags
- * @items: A list of struct kdbus_item
- *
- * Note that the user is responsible for freeing the allocated memory with
- * the KDBUS_CMD_FREE ioctl.
- */
-struct kdbus_conn_info {
- __u64 size;
- __u64 id;
- __u64 flags;
- struct kdbus_item items[0];
-};
-
-/**
- * struct kdbus_cmd_match - struct to add or remove matches
- * @size: The total size of the struct
- * @owner_id: Privileged users may (de)register matches on behalf
- * of other peers
- * @cookie: Userspace supplied cookie. When removing, the cookie
- * identifies the match to remove
- * @items: A list of items for additional information
- *
- * This structure is used with the KDBUS_CMD_ADD_MATCH and
- * KDBUS_CMD_REMOVE_MATCH ioctl.
- */
-struct kdbus_cmd_match {
- __u64 size;
- __u64 owner_id;
- __u64 cookie;
- struct kdbus_item items[0];
-} __attribute__((aligned(8)));
-
-/**
- * struct kdbus_cmd_memfd_make - create a kdbus memfd
- * @size: The total size of the struct
- * @file_size: The initial file size
- * @fd: The returned file descriptor number
- * @__pad: Padding to ensure proper alignement
- * @items: A list of items for additional information
- *
- * This structure is used with the KDBUS_CMD_MEMFD_NEW ioctl.
- */
-struct kdbus_cmd_memfd_make {
- __u64 size;
- __u64 file_size;
- int fd;
- __u32 __pad;
- struct kdbus_item items[0];
-} __attribute__((aligned(8)));
-
-/**
- * enum kdbus_ioctl_type - Ioctl API
- * @KDBUS_CMD_BUS_MAKE: After opening the "control" device node, this
- * command creates a new bus with the specified
- * name. The bus is immediately shut down and
- * cleaned up when the opened "control" device node
- * is closed.
- * @KDBUS_CMD_NS_MAKE: Similar to KDBUS_CMD_BUS_MAKE, but it creates a
- * new kdbus namespace.
- * @KDBUS_CMD_EP_MAKE: Creates a new named special endpoint to talk to
- * the bus. Such endpoints usually carry a more
- * restrictive policy and grant restricted access
- * to specific applications.
- * @KDBUS_CMD_HELLO: By opening the bus device node a connection is
- * created. After a HELLO the opened connection
- * becomes an active peer on the bus.
- * @KDBUS_CMD_BYEBYE: Disconnect a connection. If the connection's
- * message list is empty, the calls succeeds, and
- * the handle is rendered unusable. Otherwise,
- * -EAGAIN is returned without any further side-
- * effects.
- * @KDBUS_CMD_MSG_SEND: Send a message and pass data from userspace to
- * the kernel.
- * @KDBUS_CMD_MSG_RECV: Receive a message from the kernel which is
- * placed in the receiver's pool.
- * @KDBUS_CMD_FREE: Release the allocated memory in the receiver's
- * pool.
- * @KDBUS_CMD_NAME_ACQUIRE: Request a well-known bus name to associate with
- * the connection. Well-known names are used to
- * address a peer on the bus.
- * @KDBUS_CMD_NAME_RELEASE: Release a well-known name the connection
- * currently owns.
- * @KDBUS_CMD_NAME_LIST: Retrieve the list of all currently registered
- * well-known and unique names.
- * @KDBUS_CMD_CONN_INFO: Retrieve credentials and properties of the
- * initial creator of the connection. The data was
- * stored at registration time and does not
- * necessarily represent the connected process or
- * the actual state of the process.
- * @KDBUS_CMD_MATCH_ADD: Install a match which broadcast messages should
- * be delivered to the connection.
- * @KDBUS_CMD_MATCH_REMOVE: Remove a current match for broadcast messages.
- * @KDBUS_CMD_EP_POLICY_SET: Set the policy of an endpoint. It is used to
- * restrict the access for endpoints created with
- * KDBUS_CMD_EP_MAKE.
- * @KDBUS_CMD_MEMFD_NEW: Return a new file descriptor which provides an
- * anonymous shared memory file and which can be
- * used to pass around larger chunks of data.
- * Kdbus memfd files can be sealed, which allows
- * the receiver to trust the data it has received.
- * Kdbus memfd files expose only very limited
- * operations, they can be mmap()ed, seek()ed,
- * (p)read(v)() and (p)write(v)(); most other
- * common file operations are not implemented.
- * Special caution needs to be taken with
- * read(v)()/write(v)() on a shared file; the
- * underlying file position is always shared
- * between all users of the file and race against
- * each other, pread(v)()/pwrite(v)() avoid these
- * issues.
- * @KDBUS_CMD_MEMFD_SIZE_GET: Return the size of the underlying file, which
- * changes with write().
- * @KDBUS_CMD_MEMFD_SIZE_SET: Truncate the underlying file to the specified
- * size.
- * @KDBUS_CMD_MEMFD_SEAL_GET: Return the state of the file sealing.
- * @KDBUS_CMD_MEMFD_SEAL_SET: Seal or break a seal of the file. Only files
- * which are not shared with other processes and
- * which are currently not mapped can be sealed.
- * The current process needs to be the one and
- * single owner of the file, the sealing cannot
- * be changed as long as the file is shared.
- */
-enum kdbus_ioctl_type {
- KDBUS_CMD_BUS_MAKE = _IOW (KDBUS_IOC_MAGIC, 0x00, struct kdbus_cmd_make),
- KDBUS_CMD_NS_MAKE = _IOR (KDBUS_IOC_MAGIC, 0x10, struct kdbus_cmd_make),
- KDBUS_CMD_EP_MAKE = _IOW (KDBUS_IOC_MAGIC, 0x20, struct kdbus_cmd_make),
-
- KDBUS_CMD_HELLO = _IOWR(KDBUS_IOC_MAGIC, 0x30, struct kdbus_cmd_hello),
- KDBUS_CMD_BYEBYE = _IO (KDBUS_IOC_MAGIC, 0x31),
-
- KDBUS_CMD_MSG_SEND = _IOW (KDBUS_IOC_MAGIC, 0x40, struct kdbus_msg),
- KDBUS_CMD_MSG_RECV = _IOWR(KDBUS_IOC_MAGIC, 0x41, struct kdbus_cmd_recv),
- KDBUS_CMD_FREE = _IOW (KDBUS_IOC_MAGIC, 0x42, __u64 *),
-
- KDBUS_CMD_NAME_ACQUIRE = _IOWR(KDBUS_IOC_MAGIC, 0x50, struct kdbus_cmd_name),
- KDBUS_CMD_NAME_RELEASE = _IOW (KDBUS_IOC_MAGIC, 0x51, struct kdbus_cmd_name),
- KDBUS_CMD_NAME_LIST = _IOWR(KDBUS_IOC_MAGIC, 0x52, struct kdbus_cmd_name_list),
-
- KDBUS_CMD_CONN_INFO = _IOWR(KDBUS_IOC_MAGIC, 0x60, struct kdbus_cmd_conn_info),
-
- KDBUS_CMD_MATCH_ADD = _IOW (KDBUS_IOC_MAGIC, 0x70, struct kdbus_cmd_match),
- KDBUS_CMD_MATCH_REMOVE = _IOW (KDBUS_IOC_MAGIC, 0x71, struct kdbus_cmd_match),
-
- KDBUS_CMD_EP_POLICY_SET = _IOW (KDBUS_IOC_MAGIC, 0x80, struct kdbus_cmd_policy),
-
- KDBUS_CMD_MEMFD_NEW = _IOWR(KDBUS_IOC_MAGIC, 0xc0, struct kdbus_cmd_memfd_make),
- KDBUS_CMD_MEMFD_SIZE_GET = _IOR (KDBUS_IOC_MAGIC, 0xc1, __u64 *),
- KDBUS_CMD_MEMFD_SIZE_SET = _IOW (KDBUS_IOC_MAGIC, 0xc2, __u64 *),
- KDBUS_CMD_MEMFD_SEAL_GET = _IOR (KDBUS_IOC_MAGIC, 0xc3, int *),
- KDBUS_CMD_MEMFD_SEAL_SET = _IO (KDBUS_IOC_MAGIC, 0xc4),
-};
-
-/*
- * errno - api error codes
- * @E2BIG: A message contains too many records or items.
- * @EADDRINUSE: A well-known bus name is already taken by another
- * connection.
- * @EADDRNOTAVAIL: A message flagged not to activate a service, addressed
- * a service which is not currently running.
- * @EAGAIN: No messages are queued at the moment.
- * @EBADF: File descriptors passed with the message are not valid.
- * @EBADFD: A bus connection is in a corrupted state.
- * @EBADMSG: Passed data contains a combination of conflicting or
- * inconsistent types.
- * @EBUSY: The user tried to say BYEBYE to a connection, but the
- * connection had a non-empty message list.
- * @ECONNRESET: A connection is shut down, no further operations are
- * possible.
- * @ECOMM: A peer does not accept the file descriptors addressed
- * to it.
- * @EDESTADDRREQ: The well-known bus name is required but missing.
- * @EDOM: The size of data does not match the expectations. Used
- * for the size of the bloom filter bit field.
- * @EEXIST: A requested namespace, bus or endpoint with the same
- * name already exists. A specific data type, which is
- * only expected once, is provided multiple times.
- * @EFAULT: The supplied memory could not be accessed, or the data
- * is not properly aligned.
- * @EINVAL: The provided data does not match its type or other
- * expectations, like a string which is not NUL terminated,
- * or a string length that points behind the first
- * \0-byte in the string.
- * @EMEDIUMTYPE: A file descriptor which is not a kdbus memfd was
- * refused to send as KDBUS_MSG_PAYLOAD_MEMFD.
- * @EMFILE: Too many file descriptors have been supplied with a
- * message.
- * @EMLINK: Too many requests from this connection to other peers
- * are queued and waiting for a reply
- * @EMSGSIZE: The supplied data is larger than the allowed maximum
- * size.
- * @ENAMETOOLONG: The requested name is larger than the allowed maximum
- * size.
- * @ENOBUFS: There is no space left for the submitted data to fit
- * into the receiver's pool.
- * @ENOENT: The name to query information about is currently not on
- * the bus.
- * @ENOMEM: Out of memory.
- * @ENOMSG: The queue is not empty, but no message with a matching
- * priority is currently queued.
- * @ENOSYS: The requested functionality is not available.
- * @ENOTSUPP: The feature negotiation failed, a not supported feature
- * was requested, or an unknown item type was received.
- * @ENOTTY: An unknown ioctl command was received.
- * @ENOTUNIQ: A specific data type was addressed to a broadcast
- * address, but only direct addresses support this kind of
- * data.
- * @ENXIO: A unique address does not exist, or an offset in the
- * receiver's pool does not represent a queued message.
- * @EPERM: The policy prevented an operation. The requested
- * resource is owned by another entity.
- * @ESHUTDOWN: A namespace or endpoint is currently shutting down;
- * no further operations will be possible.
- * @ESRCH: A requested well-known bus name is not found.
- * @ETXTBSY: A kdbus memfd file cannot be sealed or the seal removed,
- * because it is shared with other processes or still
- * mmap()ed.
- * @EXFULL: The size limits in the pool are reached, no data of
- * the size tried to submit can be queued.
- */
-#endif
diff --git a/src/libsystemd/resolve-util.h b/src/libsystemd/resolve-util.h
deleted file mode 100644
index 58df738..0000000
--- a/src/libsystemd/resolve-util.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Daniel Buch
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_resolve*, sd_resolve_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct addrinfo*, sd_resolve_freeaddrinfo);
-#define _cleanup_resolve_free_ _cleanup_(sd_resolve_freep)
-#define _cleanup_resolve_addrinfo_free_ _cleanup_(sd_resolve_freeaddrinfop)
diff --git a/src/libsystemd/rtnl-internal.h b/src/libsystemd/rtnl-internal.h
deleted file mode 100644
index 2e0b7b8..0000000
--- a/src/libsystemd/rtnl-internal.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Tom Gundersen <teg at jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <linux/netlink.h>
-
-#include "refcnt.h"
-#include "prioq.h"
-#include "list.h"
-
-#include "sd-rtnl.h"
-
-struct reply_callback {
- sd_rtnl_message_handler_t callback;
- void *userdata;
- usec_t timeout;
- uint64_t serial;
- unsigned prioq_idx;
-};
-
-struct match_callback {
- sd_rtnl_message_handler_t callback;
- uint16_t type;
- void *userdata;
-
- LIST_FIELDS(struct match_callback, match_callbacks);
-};
-
-struct sd_rtnl {
- RefCount n_ref;
-
- int fd;
-
- union {
- struct sockaddr sa;
- struct sockaddr_nl nl;
- } sockaddr;
-
- sd_rtnl_message **rqueue;
- unsigned rqueue_size;
-
- sd_rtnl_message **wqueue;
- unsigned wqueue_size;
-
- bool processing:1;
-
- uint32_t serial;
-
- struct Prioq *reply_callbacks_prioq;
- Hashmap *reply_callbacks;
-
- LIST_HEAD(struct match_callback, match_callbacks);
-
- pid_t original_pid;
-
- sd_event_source *io_event_source;
- sd_event_source *time_event_source;
- sd_event_source *exit_event_source;
- sd_event *event;
-};
-
-#define RTNL_DEFAULT_TIMEOUT ((usec_t) (10 * USEC_PER_SEC))
-
-#define RTNL_WQUEUE_MAX 1024
-#define RTNL_RQUEUE_MAX 64*1024
-
-int message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message **ret);
-uint32_t message_get_serial(sd_rtnl_message *m);
-int message_seal(sd_rtnl *nl, sd_rtnl_message *m);
-
-bool message_type_is_link(uint16_t type);
-bool message_type_is_addr(uint16_t type);
-bool message_type_is_route(uint16_t type);
-
-int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m);
-int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret);
-
-/* Make sure callbacks don't destroy the rtnl connection */
-#define RTNL_DONT_DESTROY(rtnl) \
- _cleanup_sd_rtnl_unref_ _unused_ sd_rtnl *_dont_destroy_##rtnl = sd_rtnl_ref(rtnl)
diff --git a/src/libsystemd/rtnl-message.c b/src/libsystemd/rtnl-message.c
deleted file mode 100644
index 984115c..0000000
--- a/src/libsystemd/rtnl-message.c
+++ /dev/null
@@ -1,887 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Tom Gundersen <teg at jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <linux/rtnetlink.h>
-#include <netinet/in.h>
-#include <netinet/ether.h>
-#include <stdbool.h>
-#include <unistd.h>
-
-#include "util.h"
-#include "refcnt.h"
-
-#include "sd-rtnl.h"
-#include "rtnl-internal.h"
-
-struct sd_rtnl_message {
- RefCount n_ref;
-
- struct nlmsghdr *hdr;
- size_t container_offset; /* offset from hdr to container start */
- size_t next_rta_offset; /* offset from hdr to next rta */
-
- bool sealed:1;
-};
-
-#define CURRENT_CONTAINER(m) ((m)->container_offset ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->container_offset) : NULL)
-#define NEXT_RTA(m) ((struct rtattr*)((uint8_t*)(m)->hdr + (m)->next_rta_offset))
-#define UPDATE_RTA(m, new) (m)->next_rta_offset = (uint8_t*)(new) - (uint8_t*)(m)->hdr;
-
-static int message_new(sd_rtnl_message **ret, size_t initial_size) {
- sd_rtnl_message *m;
-
- assert_return(ret, -EINVAL);
- assert_return(initial_size >= sizeof(struct nlmsghdr), -EINVAL);
-
- m = new0(sd_rtnl_message, 1);
- if (!m)
- return -ENOMEM;
-
- m->hdr = malloc0(initial_size);
- if (!m->hdr) {
- free(m);
- return -ENOMEM;
- }
-
- m->n_ref = REFCNT_INIT;
-
- m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
- m->sealed = false;
-
- *ret = m;
-
- return 0;
-}
-
-int message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message **ret) {
- struct nlmsgerr *err;
- int r;
-
- assert(error <= 0);
-
- r = message_new(ret, NLMSG_SPACE(sizeof(struct nlmsgerr)));
- if (r < 0)
- return r;
-
- (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
- (*ret)->hdr->nlmsg_type = NLMSG_ERROR;
- (*ret)->hdr->nlmsg_seq = serial;
-
- err = NLMSG_DATA((*ret)->hdr);
-
- err->error = error;
-
- return 0;
-}
-
-bool message_type_is_route(uint16_t type) {
- switch (type) {
- case RTM_NEWROUTE:
- case RTM_GETROUTE:
- case RTM_DELROUTE:
- return true;
- default:
- return false;
- }
-}
-
-bool message_type_is_link(uint16_t type) {
- switch (type) {
- case RTM_NEWLINK:
- case RTM_SETLINK:
- case RTM_GETLINK:
- case RTM_DELLINK:
- return true;
- default:
- return false;
- }
-}
-
-bool message_type_is_addr(uint16_t type) {
- switch (type) {
- case RTM_NEWADDR:
- case RTM_GETADDR:
- case RTM_DELADDR:
- return true;
- default:
- return false;
- }
-}
-
-int sd_rtnl_message_route_set_dst_prefixlen(sd_rtnl_message *m, unsigned char prefixlen) {
- struct rtmsg *rtm;
-
- rtm = NLMSG_DATA(m->hdr);
-
- rtm->rtm_dst_len = prefixlen;
-
- return 0;
-}
-
-int sd_rtnl_message_route_new(uint16_t nlmsg_type, unsigned char rtm_family,
- sd_rtnl_message **ret) {
- struct rtmsg *rtm;
- int r;
-
- assert_return(message_type_is_route(nlmsg_type), -EINVAL);
- assert_return(rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = message_new(ret, NLMSG_SPACE(sizeof(struct rtmsg)));
- if (r < 0)
- return r;
-
- (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
- (*ret)->hdr->nlmsg_type = nlmsg_type;
- if (nlmsg_type == RTM_NEWROUTE)
- (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
-
- rtm = NLMSG_DATA((*ret)->hdr);
-
- UPDATE_RTA(*ret, RTM_RTA(rtm));
-
- rtm->rtm_family = rtm_family;
- rtm->rtm_scope = RT_SCOPE_UNIVERSE;
- rtm->rtm_type = RTN_UNICAST;
- rtm->rtm_table = RT_TABLE_MAIN;
- rtm->rtm_protocol = RTPROT_BOOT;
-
- return 0;
-}
-
-int sd_rtnl_message_link_set_flags(sd_rtnl_message *m, unsigned flags) {
- struct ifinfomsg *ifi;
-
- ifi = NLMSG_DATA(m->hdr);
-
- ifi->ifi_flags = flags;
- ifi->ifi_change = 0xffffffff;
-
- return 0;
-}
-
-int sd_rtnl_message_link_set_type(sd_rtnl_message *m, unsigned type) {
- struct ifinfomsg *ifi;
-
- ifi = NLMSG_DATA(m->hdr);
-
- ifi->ifi_type = type;
-
- return 0;
-}
-
-int sd_rtnl_message_link_new(uint16_t nlmsg_type, int index, sd_rtnl_message **ret) {
- struct ifinfomsg *ifi;
- int r;
-
- assert_return(message_type_is_link(nlmsg_type), -EINVAL);
- assert_return(nlmsg_type == RTM_NEWLINK || index > 0, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = message_new(ret, NLMSG_SPACE(sizeof(struct ifinfomsg)));
- if (r < 0)
- return r;
-
- (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- (*ret)->hdr->nlmsg_type = nlmsg_type;
- if (nlmsg_type == RTM_NEWLINK)
- (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE;
-
- ifi = NLMSG_DATA((*ret)->hdr);
-
- ifi->ifi_family = AF_UNSPEC;
- ifi->ifi_index = index;
-
- UPDATE_RTA(*ret, IFLA_RTA(ifi));
-
- return 0;
-}
-
-int sd_rtnl_message_addr_new(uint16_t nlmsg_type, int index, unsigned char family, unsigned char prefixlen, unsigned char flags, unsigned char scope, sd_rtnl_message **ret) {
- struct ifaddrmsg *ifa;
- int r;
-
- assert_return(message_type_is_addr(nlmsg_type), -EINVAL);
- assert_return(index > 0, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = message_new(ret, NLMSG_SPACE(sizeof(struct ifaddrmsg)));
- if (r < 0)
- return r;
-
- (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
- (*ret)->hdr->nlmsg_type = nlmsg_type;
-
- ifa = NLMSG_DATA((*ret)->hdr);
-
- ifa->ifa_family = family;
- ifa->ifa_prefixlen = prefixlen;
- ifa->ifa_flags = flags;
- ifa->ifa_scope = scope;
- ifa->ifa_index = index;
-
- UPDATE_RTA(*ret, IFA_RTA(ifa));
-
- return 0;
-}
-
-sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
- if (m)
- assert_se(REFCNT_INC(m->n_ref) >= 2);
-
- return m;
-}
-
-sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) {
- if (m && REFCNT_DEC(m->n_ref) <= 0) {
- free(m->hdr);
- free(m);
- }
-
- return NULL;
-}
-
-int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type) {
- assert_return(m, -EINVAL);
- assert_return(type, -EINVAL);
-
- *type = m->hdr->nlmsg_type;
-
- return 0;
-}
-
-int sd_rtnl_message_link_get_ifindex(sd_rtnl_message *m, int *ifindex) {
- struct ifinfomsg *ifi;
-
- assert_return(m, -EINVAL);
- assert_return(m->hdr, -EINVAL);
- assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
- assert_return(ifindex, -EINVAL);
-
- ifi = NLMSG_DATA(m->hdr);
-
- *ifindex = ifi->ifi_index;
-
- return 0;
-}
-
-int sd_rtnl_message_link_get_flags(sd_rtnl_message *m, unsigned *flags) {
- struct ifinfomsg *ifi;
-
- assert_return(m, -EINVAL);
- assert_return(m->hdr, -EINVAL);
- assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
- assert_return(flags, -EINVAL);
-
- ifi = NLMSG_DATA(m->hdr);
-
- *flags = ifi->ifi_flags;
-
- return 0;
-}
-
-/* If successful the updated message will be correctly aligned, if
- unsuccessful the old message is untouched. */
-static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, size_t data_length) {
- uint32_t rta_length, message_length;
- struct nlmsghdr *new_hdr;
- struct rtattr *rta;
- char *padding;
-
- assert(m);
- assert(m->hdr);
- assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len);
- assert(!data || data_length > 0);
-
- /* get the size of the new rta attribute (with padding at the end) */
- rta_length = RTA_LENGTH(data_length);
-
- /* get the new message size (with padding at the end) */
- message_length = m->hdr->nlmsg_len + RTA_ALIGN(rta_length);
-
- /* realloc to fit the new attribute */
- new_hdr = realloc(m->hdr, message_length);
- if (!new_hdr)
- return -ENOMEM;
- m->hdr = new_hdr;
-
- /* get pointer to the attribute we are about to add */
- rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
-
- /* if we are inside a container, extend it */
- if (CURRENT_CONTAINER(m))
- CURRENT_CONTAINER(m)->rta_len += message_length - m->hdr->nlmsg_len;
-
- /* fill in the attribute */
- rta->rta_type = type;
- rta->rta_len = rta_length;
- if (!data) {
- /* this is the start of a new container */
- m->container_offset = m->hdr->nlmsg_len;
- } else {
- /* we don't deal with the case where the user lies about the type
- * and gives us too little data (so don't do that)
- */
- padding = mempcpy(RTA_DATA(rta), data, data_length);
- /* make sure also the padding at the end of the message is initialized */
- memzero(padding,
- (uint8_t *) m->hdr + message_length - (uint8_t *) padding);
- }
-
- /* update message size */
- m->hdr->nlmsg_len = message_length;
-
- return 0;
-}
-
-int sd_rtnl_message_append_string(sd_rtnl_message *m, unsigned short type, const char *data) {
- uint16_t rtm_type;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(data, -EINVAL);
-
- r = sd_rtnl_message_get_type(m, &rtm_type);
- if (r < 0)
- return r;
-
- /* check that the type is correct */
- switch (rtm_type) {
- case RTM_NEWLINK:
- case RTM_SETLINK:
- case RTM_GETLINK:
- case RTM_DELLINK:
- if (CURRENT_CONTAINER(m)) {
- if (CURRENT_CONTAINER(m)->rta_type != IFLA_LINKINFO ||
- type != IFLA_INFO_KIND)
- return -ENOTSUP;
- } else {
- switch (type) {
- case IFLA_IFNAME:
- case IFLA_IFALIAS:
- case IFLA_QDISC:
- break;
- default:
- return -ENOTSUP;
- }
- }
- break;
- case RTM_NEWADDR:
- case RTM_GETADDR:
- case RTM_DELADDR:
- if (type != IFA_LABEL)
- return -ENOTSUP;
- break;
- default:
- return -ENOTSUP;
- }
-
- r = add_rtattr(m, type, data, strlen(data) + 1);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int sd_rtnl_message_append_u32(sd_rtnl_message *m, unsigned short type, uint32_t data) {
- uint16_t rtm_type;
- int r;
-
- assert_return(m, -EINVAL);
-
- r = sd_rtnl_message_get_type(m, &rtm_type);
- if (r < 0)
- return r;
-
- /* check that the type is correct */
- switch (rtm_type) {
- case RTM_NEWLINK:
- case RTM_SETLINK:
- case RTM_GETLINK:
- case RTM_DELLINK:
- switch (type) {
- case IFLA_MASTER:
- case IFLA_MTU:
- case IFLA_LINK:
- break;
- default:
- return -ENOTSUP;
- }
- break;
- case RTM_NEWROUTE:
- case RTM_GETROUTE:
- case RTM_DELROUTE:
- switch (type) {
- case RTA_TABLE:
- case RTA_PRIORITY:
- case RTA_IIF:
- case RTA_OIF:
- break;
- default:
- return -ENOTSUP;
- }
- break;
- default:
- return -ENOTSUP;
- }
-
- r = add_rtattr(m, type, &data, sizeof(uint32_t));
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int sd_rtnl_message_append_in_addr(sd_rtnl_message *m, unsigned short type, const struct in_addr *data) {
- struct ifaddrmsg *ifa;
- struct rtmsg *rtm;
- uint16_t rtm_type;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(data, -EINVAL);
-
- r = sd_rtnl_message_get_type(m, &rtm_type);
- if (r < 0)
- return r;
-
- /* check that the type is correct */
- switch (rtm_type) {
- case RTM_NEWADDR:
- case RTM_GETADDR:
- case RTM_DELADDR:
- switch (type) {
- case IFA_ADDRESS:
- case IFA_LOCAL:
- case IFA_BROADCAST:
- case IFA_ANYCAST:
- ifa = NLMSG_DATA(m->hdr);
-
- if (ifa->ifa_family != AF_INET)
- return -EINVAL;
-
- break;
- default:
- return -ENOTSUP;
- }
- break;
- case RTM_NEWROUTE:
- case RTM_GETROUTE:
- case RTM_DELROUTE:
- switch (type) {
- case RTA_DST:
- case RTA_SRC:
- case RTA_GATEWAY:
- rtm = NLMSG_DATA(m->hdr);
-
- if (rtm->rtm_family != AF_INET)
- return -EINVAL;
-
- break;
- default:
- return -ENOTSUP;
- }
- break;
- default:
- return -ENOTSUP;
- }
-
- r = add_rtattr(m, type, data, sizeof(struct in_addr));
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int sd_rtnl_message_append_in6_addr(sd_rtnl_message *m, unsigned short type, const struct in6_addr *data) {
- struct ifaddrmsg *ifa;
- struct rtmsg *rtm;
- uint16_t rtm_type;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(data, -EINVAL);
-
- r = sd_rtnl_message_get_type(m, &rtm_type);
- if (r < 0)
- return r;
-
- /* check that the type is correct */
- switch (rtm_type) {
- case RTM_NEWADDR:
- case RTM_GETADDR:
- case RTM_DELADDR:
- switch (type) {
- case IFA_ADDRESS:
- case IFA_LOCAL:
- case IFA_BROADCAST:
- case IFA_ANYCAST:
- ifa = NLMSG_DATA(m->hdr);
-
- if (ifa->ifa_family != AF_INET6)
- return -EINVAL;
-
- break;
- default:
- return -ENOTSUP;
- }
- break;
- case RTM_NEWROUTE:
- case RTM_GETROUTE:
- case RTM_DELROUTE:
- switch (type) {
- case RTA_DST:
- case RTA_SRC:
- case RTA_GATEWAY:
- rtm = NLMSG_DATA(m->hdr);
-
- if (rtm->rtm_family != AF_INET6)
- return -EINVAL;
-
- break;
- default:
- return -ENOTSUP;
- }
- default:
- return -ENOTSUP;
- }
-
- r = add_rtattr(m, type, data, sizeof(struct in6_addr));
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int sd_rtnl_message_append_ether_addr(sd_rtnl_message *m, unsigned short type, const struct ether_addr *data) {
- uint16_t rtm_type;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(data, -EINVAL);
-
- sd_rtnl_message_get_type(m, &rtm_type);
-
- switch (rtm_type) {
- case RTM_NEWLINK:
- case RTM_SETLINK:
- case RTM_DELLINK:
- case RTM_GETLINK:
- switch (type) {
- case IFLA_ADDRESS:
- case IFLA_BROADCAST:
- break;
- default:
- return -ENOTSUP;
- }
- break;
- default:
- return -ENOTSUP;
- }
-
- r = add_rtattr(m, type, data, ETH_ALEN);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) {
- uint16_t rtm_type;
-
- assert_return(m, -EINVAL);
- assert_return(!CURRENT_CONTAINER(m), -EINVAL);
-
- sd_rtnl_message_get_type(m, &rtm_type);
-
- if (message_type_is_link(rtm_type)) {
- if (type == IFLA_LINKINFO)
- return add_rtattr(m, type, NULL, 0);
- else
- return -ENOTSUP;
- } else
- return -ENOTSUP;
-
- return 0;
-}
-
-int sd_rtnl_message_close_container(sd_rtnl_message *m) {
- assert_return(m, -EINVAL);
- assert_return(CURRENT_CONTAINER(m), -EINVAL);
-
- m->container_offset = 0;
-
- return 0;
-}
-
-int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
- size_t remaining_size;
- uint16_t rtm_type;
- int r;
-
- assert(m);
- assert(m->next_rta_offset);
- assert(type);
- assert(data);
-
- remaining_size = m->hdr->nlmsg_len - m->next_rta_offset;
-
- if (!RTA_OK(NEXT_RTA(m), remaining_size))
- return 0;
-
- /* make sure we don't try to read a container
- * TODO: add support for entering containers for reading */
- r = sd_rtnl_message_get_type(m, &rtm_type);
- if (r < 0)
- return r;
-
- if (message_type_is_link(rtm_type) &&
- NEXT_RTA(m)->rta_type == IFLA_LINKINFO)
- return -EINVAL;
-
- *data = RTA_DATA(NEXT_RTA(m));
- *type = NEXT_RTA(m)->rta_type;
-
- UPDATE_RTA(m, RTA_NEXT(NEXT_RTA(m), remaining_size));
-
- return 1;
-}
-
-uint32_t message_get_serial(sd_rtnl_message *m) {
- assert(m);
- assert(m->hdr);
-
- return m->hdr->nlmsg_seq;
-}
-
-int sd_rtnl_message_get_errno(sd_rtnl_message *m) {
- struct nlmsgerr *err;
-
- assert_return(m, -EINVAL);
- assert_return(m->hdr, -EINVAL);
-
- if (m->hdr->nlmsg_type != NLMSG_ERROR)
- return 0;
-
- err = NLMSG_DATA(m->hdr);
-
- return err->error;
-}
-
-int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
- assert(nl);
- assert(m);
- assert(m->hdr);
-
- if (m->sealed)
- return -EPERM;
-
- m->hdr->nlmsg_seq = nl->serial++;
- m->sealed = true;
-
- return 0;
-}
-
-static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
- assert(rtnl);
- assert(need);
-
- /* ioctl(rtnl->fd, FIONREAD, &need)
- Does not appear to work on netlink sockets. libnl uses
- MSG_PEEK instead. I don't know if that is worth the
- extra roundtrip.
-
- For now we simply use the maximum message size the kernel
- may use (NLMSG_GOODSIZE), and then realloc to the actual
- size after reading the message (hence avoiding huge memory
- usage in case many small messages are kept around) */
- *need = page_size();
- if (*need > 8192UL)
- *need = 8192UL;
-
- return 0;
-}
-
-/* returns the number of bytes sent, or a negative error code */
-int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
- union {
- struct sockaddr sa;
- struct sockaddr_nl nl;
- } addr = {
- .nl.nl_family = AF_NETLINK,
- };
- ssize_t k;
-
- assert(nl);
- assert(m);
- assert(m->hdr);
-
- k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
- 0, &addr.sa, sizeof(addr));
- if (k < 0)
- return (errno == EAGAIN) ? 0 : -errno;
-
- return k;
-}
-
-/* On success, the number of bytes received is returned and *ret points to the received message
- * which has a valid header and the correct size.
- * If nothing useful was received 0 is returned.
- * On failure, a negative error code is returned.
- */
-int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
- sd_rtnl_message *m;
- union {
- struct sockaddr sa;
- struct sockaddr_nl nl;
- } addr;
- socklen_t addr_len;
- int r;
- ssize_t k;
- size_t need;
-
- assert(nl);
- assert(ret);
-
- r = message_receive_need(nl, &need);
- if (r < 0)
- return r;
-
- r = message_new(&m, need);
- if (r < 0)
- return r;
-
- addr_len = sizeof(addr);
-
- k = recvfrom(nl->fd, m->hdr, need,
- 0, &addr.sa, &addr_len);
- if (k < 0)
- k = (errno == EAGAIN) ? 0 : -errno; /* no data */
- else if (k == 0)
- k = -ECONNRESET; /* connection was closed by the kernel */
- else if (addr_len != sizeof(addr.nl) ||
- addr.nl.nl_family != AF_NETLINK)
- k = -EIO; /* not a netlink message */
- else if (addr.nl.nl_pid != 0)
- k = 0; /* not from the kernel */
- else if ((size_t) k < sizeof(struct nlmsghdr) ||
- (size_t) k < m->hdr->nlmsg_len)
- k = -EIO; /* too small (we do accept too big though) */
- else if (m->hdr->nlmsg_pid && m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
- k = 0; /* not broadcast and not for us */
-
- if (k > 0)
- switch (m->hdr->nlmsg_type) {
- struct ifinfomsg *ifi;
- struct ifaddrmsg *ifa;
- struct rtmsg *rtm;
-
- /* check that the size matches the message type */
- case NLMSG_ERROR:
- if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
- k = -EIO;
- break;
- case RTM_NEWLINK:
- case RTM_SETLINK:
- case RTM_DELLINK:
- case RTM_GETLINK:
- if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
- k = -EIO;
- else {
- ifi = NLMSG_DATA(m->hdr);
- UPDATE_RTA(m, IFLA_RTA(ifi));
- }
- break;
- case RTM_NEWADDR:
- case RTM_DELADDR:
- case RTM_GETADDR:
- if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
- k = -EIO;
- else {
- ifa = NLMSG_DATA(m->hdr);
- UPDATE_RTA(m, IFA_RTA(ifa));
- }
- break;
- case RTM_NEWROUTE:
- case RTM_DELROUTE:
- case RTM_GETROUTE:
- if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg)))
- k = -EIO;
- else {
- rtm = NLMSG_DATA(m->hdr);
- UPDATE_RTA(m, RTM_RTA(rtm));
- }
- break;
- case NLMSG_NOOP:
- k = 0;
- break;
- default:
- k = 0; /* ignoring message of unknown type */
- }
-
- if (k <= 0)
- sd_rtnl_message_unref(m);
- else {
- /* we probably allocated way too much memory, give it back */
- m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);
- *ret = m;
- }
-
- return k;
-}
-
-int sd_rtnl_message_rewind(sd_rtnl_message *m) {
- struct ifinfomsg *ifi;
- struct ifaddrmsg *ifa;
- struct rtmsg *rtm;
-
- assert_return(m, -EINVAL);
- assert_return(m->hdr, -EINVAL);
-
- switch(m->hdr->nlmsg_type) {
- case RTM_NEWLINK:
- case RTM_SETLINK:
- case RTM_GETLINK:
- case RTM_DELLINK:
- ifi = NLMSG_DATA(m->hdr);
- UPDATE_RTA(m, IFLA_RTA(ifi));
-
- break;
- case RTM_NEWADDR:
- case RTM_GETADDR:
- case RTM_DELADDR:
- ifa = NLMSG_DATA(m->hdr);
- UPDATE_RTA(m, IFA_RTA(ifa));
-
- break;
- case RTM_NEWROUTE:
- case RTM_GETROUTE:
- case RTM_DELROUTE:
- rtm = NLMSG_DATA(m->hdr);
- UPDATE_RTA(m, RTM_RTA(rtm));
-
- break;
- default:
- return -ENOTSUP;
- }
-
- return 0;
-}
diff --git a/src/libsystemd/rtnl-util.c b/src/libsystemd/rtnl-util.c
deleted file mode 100644
index dfc0050..0000000
--- a/src/libsystemd/rtnl-util.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2013 Tom Gundersen <teg at jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <linux/rtnetlink.h>
-#include <netinet/ether.h>
-
-#include "sd-rtnl.h"
-
-#include "rtnl-util.h"
-
-int rtnl_set_link_name(sd_rtnl *rtnl, int ifindex, const char *name) {
- _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message = NULL;
- int r;
-
- assert(rtnl);
- assert(ifindex > 0);
- assert(name);
-
- r = sd_rtnl_message_link_new(RTM_SETLINK, ifindex, &message);
- if (r < 0)
- return r;
-
- r = sd_rtnl_message_append_string(message, IFLA_IFNAME, name);
- if (r < 0)
- return r;
-
- r = sd_rtnl_call(rtnl, message, 0, NULL);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int rtnl_set_link_properties(sd_rtnl *rtnl, int ifindex, const char *alias,
- const struct ether_addr *mac, unsigned mtu) {
- _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message = NULL;
- bool need_update = false;
- int r;
-
- assert(rtnl);
- assert(ifindex > 0);
-
- if (!alias && !mac && mtu == 0)
- return 0;
-
- r = sd_rtnl_message_link_new(RTM_SETLINK, ifindex, &message);
- if (r < 0)
- return r;
-
- if (alias) {
- r = sd_rtnl_message_append_string(message, IFLA_IFALIAS, alias);
- if (r < 0)
- return r;
-
- need_update = true;
-
- }
-
- if (mac) {
- r = sd_rtnl_message_append_ether_addr(message, IFLA_ADDRESS, mac);
- if (r < 0)
- return r;
-
- need_update = true;
- }
-
- if (mtu > 0) {
- r = sd_rtnl_message_append_u32(message, IFLA_MTU, mtu);
- if (r < 0)
- return r;
-
- need_update = true;
- }
-
- if (need_update) {
- r = sd_rtnl_call(rtnl, message, 0, NULL);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
diff --git a/src/libsystemd/rtnl-util.h b/src/libsystemd/rtnl-util.h
deleted file mode 100644
index 013002d..0000000
--- a/src/libsystemd/rtnl-util.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2013 Tom Gundersen <teg at jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <netinet/ether.h>
-
-#include "util.h"
-#include "sd-rtnl.h"
-
-int rtnl_set_link_name(sd_rtnl *rtnl, int ifindex, const char *name);
-int rtnl_set_link_properties(sd_rtnl *rtnl, int ifindex, const char *alias, const struct ether_addr *mac, unsigned mtu);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_rtnl*, sd_rtnl_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_rtnl_message*, sd_rtnl_message_unref);
-
-#define _cleanup_sd_rtnl_unref_ _cleanup_(sd_rtnl_unrefp)
-#define _cleanup_sd_rtnl_message_unref_ _cleanup_(sd_rtnl_message_unrefp)
diff --git a/src/libsystemd/sd-bus.c b/src/libsystemd/sd-bus.c
deleted file mode 100644
index 95d80db..0000000
--- a/src/libsystemd/sd-bus.c
+++ /dev/null
@@ -1,3032 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <endian.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <netdb.h>
-#include <sys/poll.h>
-#include <byteswap.h>
-#include <sys/mman.h>
-#include <pthread.h>
-
-#include "util.h"
-#include "macro.h"
-#include "strv.h"
-#include "set.h"
-#include "missing.h"
-#include "def.h"
-
-#include "sd-bus.h"
-#include "bus-internal.h"
-#include "bus-message.h"
-#include "bus-type.h"
-#include "bus-socket.h"
-#include "bus-kernel.h"
-#include "bus-control.h"
-#include "bus-introspect.h"
-#include "bus-signature.h"
-#include "bus-objects.h"
-#include "bus-util.h"
-#include "bus-container.h"
-#include "bus-protocol.h"
-
-static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec);
-static int attach_io_events(sd_bus *b);
-static void detach_io_events(sd_bus *b);
-
-static void bus_close_fds(sd_bus *b) {
- assert(b);
-
- detach_io_events(b);
-
- if (b->input_fd >= 0)
- close_nointr_nofail(b->input_fd);
-
- if (b->output_fd >= 0 && b->output_fd != b->input_fd)
- close_nointr_nofail(b->output_fd);
-
- b->input_fd = b->output_fd = -1;
-}
-
-static void bus_node_destroy(sd_bus *b, struct node *n) {
- struct node_callback *c;
- struct node_vtable *v;
- struct node_enumerator *e;
-
- assert(b);
-
- if (!n)
- return;
-
- while (n->child)
- bus_node_destroy(b, n->child);
-
- while ((c = n->callbacks)) {
- LIST_REMOVE(callbacks, n->callbacks, c);
- free(c);
- }
-
- while ((v = n->vtables)) {
- LIST_REMOVE(vtables, n->vtables, v);
- free(v->interface);
- free(v);
- }
-
- while ((e = n->enumerators)) {
- LIST_REMOVE(enumerators, n->enumerators, e);
- free(e);
- }
-
- if (n->parent)
- LIST_REMOVE(siblings, n->parent->child, n);
-
- assert_se(hashmap_remove(b->nodes, n->path) == n);
- free(n->path);
- free(n);
-}
-
-static void bus_reset_queues(sd_bus *b) {
- unsigned i;
-
- assert(b);
-
- for (i = 0; i < b->rqueue_size; i++)
- sd_bus_message_unref(b->rqueue[i]);
- free(b->rqueue);
-
- for (i = 0; i < b->wqueue_size; i++)
- sd_bus_message_unref(b->wqueue[i]);
- free(b->wqueue);
-
- b->rqueue = b->wqueue = NULL;
- b->rqueue_allocated = b->wqueue_allocated = 0;
- b->rqueue_size = b->wqueue_size = 0;
-}
-
-static void bus_free(sd_bus *b) {
- struct filter_callback *f;
- struct node *n;
-
- assert(b);
-
- sd_bus_detach_event(b);
-
- bus_close_fds(b);
-
- if (b->kdbus_buffer)
- munmap(b->kdbus_buffer, KDBUS_POOL_SIZE);
-
- free(b->rbuffer);
- free(b->unique_name);
- free(b->auth_buffer);
- free(b->address);
- free(b->kernel);
- free(b->machine);
- free(b->fake_label);
- free(b->cgroup_root);
-
- free(b->exec_path);
- strv_free(b->exec_argv);
-
- close_many(b->fds, b->n_fds);
- free(b->fds);
-
- bus_reset_queues(b);
-
- hashmap_free_free(b->reply_callbacks);
- prioq_free(b->reply_callbacks_prioq);
-
- while ((f = b->filter_callbacks)) {
- LIST_REMOVE(callbacks, b->filter_callbacks, f);
- free(f);
- }
-
- bus_match_free(&b->match_callbacks);
-
- hashmap_free_free(b->vtable_methods);
- hashmap_free_free(b->vtable_properties);
-
- while ((n = hashmap_first(b->nodes)))
- bus_node_destroy(b, n);
-
- hashmap_free(b->nodes);
-
- bus_kernel_flush_memfd(b);
-
- assert_se(pthread_mutex_destroy(&b->memfd_cache_mutex) == 0);
-
- free(b);
-}
-
-_public_ int sd_bus_new(sd_bus **ret) {
- sd_bus *r;
-
- assert_return(ret, -EINVAL);
-
- r = new0(sd_bus, 1);
- if (!r)
- return -ENOMEM;
-
- r->n_ref = REFCNT_INIT;
- r->input_fd = r->output_fd = -1;
- r->message_version = 1;
- r->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME;
- r->hello_flags |= KDBUS_HELLO_ACCEPT_FD;
- r->attach_flags |= KDBUS_ATTACH_NAMES;
- r->original_pid = getpid();
-
- assert_se(pthread_mutex_init(&r->memfd_cache_mutex, NULL) == 0);
-
- /* We guarantee that wqueue always has space for at least one
- * entry */
- if (!GREEDY_REALLOC(r->wqueue, r->wqueue_allocated, 1)) {
- free(r);
- return -ENOMEM;
- }
-
- *ret = r;
- return 0;
-}
-
-_public_ int sd_bus_set_address(sd_bus *bus, const char *address) {
- char *a;
-
- assert_return(bus, -EINVAL);
- assert_return(bus->state == BUS_UNSET, -EPERM);
- assert_return(address, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- a = strdup(address);
- if (!a)
- return -ENOMEM;
-
- free(bus->address);
- bus->address = a;
-
- return 0;
-}
-
-_public_ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) {
- assert_return(bus, -EINVAL);
- assert_return(bus->state == BUS_UNSET, -EPERM);
- assert_return(input_fd >= 0, -EINVAL);
- assert_return(output_fd >= 0, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- bus->input_fd = input_fd;
- bus->output_fd = output_fd;
- return 0;
-}
-
-_public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) {
- char *p, **a;
-
- assert_return(bus, -EINVAL);
- assert_return(bus->state == BUS_UNSET, -EPERM);
- assert_return(path, -EINVAL);
- assert_return(!strv_isempty(argv), -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- p = strdup(path);
- if (!p)
- return -ENOMEM;
-
- a = strv_copy(argv);
- if (!a) {
- free(p);
- return -ENOMEM;
- }
-
- free(bus->exec_path);
- strv_free(bus->exec_argv);
-
- bus->exec_path = p;
- bus->exec_argv = a;
-
- return 0;
-}
-
-_public_ int sd_bus_set_bus_client(sd_bus *bus, int b) {
- assert_return(bus, -EINVAL);
- assert_return(bus->state == BUS_UNSET, -EPERM);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- bus->bus_client = !!b;
- return 0;
-}
-
-_public_ int sd_bus_negotiate_fds(sd_bus *bus, int b) {
- assert_return(bus, -EINVAL);
- assert_return(bus->state == BUS_UNSET, -EPERM);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- SET_FLAG(bus->hello_flags, KDBUS_HELLO_ACCEPT_FD, b);
- return 0;
-}
-
-_public_ int sd_bus_negotiate_attach_timestamp(sd_bus *bus, int b) {
- assert_return(bus, -EINVAL);
- assert_return(bus->state == BUS_UNSET, -EPERM);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- SET_FLAG(bus->attach_flags, KDBUS_ATTACH_TIMESTAMP, b);
- return 0;
-}
-
-_public_ int sd_bus_negotiate_attach_creds(sd_bus *bus, uint64_t mask) {
- assert_return(bus, -EINVAL);
- assert_return(mask <= _SD_BUS_CREDS_ALL, -EINVAL);
- assert_return(bus->state == BUS_UNSET, -EPERM);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- /* The well knowns we need unconditionally, so that matches can work */
- bus->creds_mask = mask | SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME;
-
- return kdbus_translate_attach_flags(bus->creds_mask, &bus->creds_mask);
-}
-
-_public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) {
- assert_return(bus, -EINVAL);
- assert_return(b || sd_id128_equal(server_id, SD_ID128_NULL), -EINVAL);
- assert_return(bus->state == BUS_UNSET, -EPERM);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- bus->is_server = !!b;
- bus->server_id = server_id;
- return 0;
-}
-
-_public_ int sd_bus_set_anonymous(sd_bus *bus, int b) {
- assert_return(bus, -EINVAL);
- assert_return(bus->state == BUS_UNSET, -EPERM);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- bus->anonymous_auth = !!b;
- return 0;
-}
-
-_public_ int sd_bus_set_trusted(sd_bus *bus, int b) {
- assert_return(bus, -EINVAL);
- assert_return(bus->state == BUS_UNSET, -EPERM);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- bus->trusted = !!b;
- return 0;
-}
-
-static int hello_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
- const char *s;
- int r;
-
- assert(bus);
- assert(bus->state == BUS_HELLO || bus->state == BUS_CLOSING);
- assert(reply);
-
- r = sd_bus_message_get_errno(reply);
- if (r < 0)
- return r;
- if (r > 0)
- return -r;
-
- r = sd_bus_message_read(reply, "s", &s);
- if (r < 0)
- return r;
-
- if (!service_name_is_valid(s) || s[0] != ':')
- return -EBADMSG;
-
- bus->unique_name = strdup(s);
- if (!bus->unique_name)
- return -ENOMEM;
-
- if (bus->state == BUS_HELLO)
- bus->state = BUS_RUNNING;
-
- return 1;
-}
-
-static int bus_send_hello(sd_bus *bus) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert(bus);
-
- if (!bus->bus_client || bus->is_kernel)
- return 0;
-
- r = sd_bus_message_new_method_call(
- bus,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "Hello",
- &m);
- if (r < 0)
- return r;
-
- return sd_bus_call_async(bus, m, hello_callback, NULL, 0, &bus->hello_cookie);
-}
-
-int bus_start_running(sd_bus *bus) {
- assert(bus);
-
- if (bus->bus_client && !bus->is_kernel) {
- bus->state = BUS_HELLO;
- return 1;
- }
-
- bus->state = BUS_RUNNING;
- return 1;
-}
-
-static int parse_address_key(const char **p, const char *key, char **value) {
- size_t l, n = 0, allocated = 0;
- const char *a;
- char *r = NULL;
-
- assert(p);
- assert(*p);
- assert(value);
-
- if (key) {
- l = strlen(key);
- if (strncmp(*p, key, l) != 0)
- return 0;
-
- if ((*p)[l] != '=')
- return 0;
-
- if (*value)
- return -EINVAL;
-
- a = *p + l + 1;
- } else
- a = *p;
-
- while (*a != ';' && *a != ',' && *a != 0) {
- char c;
-
- if (*a == '%') {
- int x, y;
-
- x = unhexchar(a[1]);
- if (x < 0) {
- free(r);
- return x;
- }
-
- y = unhexchar(a[2]);
- if (y < 0) {
- free(r);
- return y;
- }
-
- c = (char) ((x << 4) | y);
- a += 3;
- } else {
- c = *a;
- a++;
- }
-
- if (!GREEDY_REALLOC(r, allocated, n + 2))
- return -ENOMEM;
-
- r[n++] = c;
- }
-
- if (!r) {
- r = strdup("");
- if (!r)
- return -ENOMEM;
- } else
- r[n] = 0;
-
- if (*a == ',')
- a++;
-
- *p = a;
-
- free(*value);
- *value = r;
-
- return 1;
-}
-
-static void skip_address_key(const char **p) {
- assert(p);
- assert(*p);
-
- *p += strcspn(*p, ",");
-
- if (**p == ',')
- (*p) ++;
-}
-
-static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
- _cleanup_free_ char *path = NULL, *abstract = NULL;
- size_t l;
- int r;
-
- assert(b);
- assert(p);
- assert(*p);
- assert(guid);
-
- while (**p != 0 && **p != ';') {
- r = parse_address_key(p, "guid", guid);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- r = parse_address_key(p, "path", &path);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- r = parse_address_key(p, "abstract", &abstract);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- skip_address_key(p);
- }
-
- if (!path && !abstract)
- return -EINVAL;
-
- if (path && abstract)
- return -EINVAL;
-
- if (path) {
- l = strlen(path);
- if (l > sizeof(b->sockaddr.un.sun_path))
- return -E2BIG;
-
- b->sockaddr.un.sun_family = AF_UNIX;
- strncpy(b->sockaddr.un.sun_path, path, sizeof(b->sockaddr.un.sun_path));
- b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l;
- } else if (abstract) {
- l = strlen(abstract);
- if (l > sizeof(b->sockaddr.un.sun_path) - 1)
- return -E2BIG;
-
- b->sockaddr.un.sun_family = AF_UNIX;
- b->sockaddr.un.sun_path[0] = 0;
- strncpy(b->sockaddr.un.sun_path+1, abstract, sizeof(b->sockaddr.un.sun_path)-1);
- b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
- }
-
- return 0;
-}
-
-static int parse_tcp_address(sd_bus *b, const char **p, char **guid) {
- _cleanup_free_ char *host = NULL, *port = NULL, *family = NULL;
- int r;
- struct addrinfo *result, hints = {
- .ai_socktype = SOCK_STREAM,
- .ai_flags = AI_ADDRCONFIG,
- };
-
- assert(b);
- assert(p);
- assert(*p);
- assert(guid);
-
- while (**p != 0 && **p != ';') {
- r = parse_address_key(p, "guid", guid);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- r = parse_address_key(p, "host", &host);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- r = parse_address_key(p, "port", &port);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- r = parse_address_key(p, "family", &family);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- skip_address_key(p);
- }
-
- if (!host || !port)
- return -EINVAL;
-
- if (family) {
- if (streq(family, "ipv4"))
- hints.ai_family = AF_INET;
- else if (streq(family, "ipv6"))
- hints.ai_family = AF_INET6;
- else
- return -EINVAL;
- }
-
- r = getaddrinfo(host, port, &hints, &result);
- if (r == EAI_SYSTEM)
- return -errno;
- else if (r != 0)
- return -EADDRNOTAVAIL;
-
- memcpy(&b->sockaddr, result->ai_addr, result->ai_addrlen);
- b->sockaddr_size = result->ai_addrlen;
-
- freeaddrinfo(result);
-
- return 0;
-}
-
-static int parse_exec_address(sd_bus *b, const char **p, char **guid) {
- char *path = NULL;
- unsigned n_argv = 0, j;
- char **argv = NULL;
- size_t allocated = 0;
- int r;
-
- assert(b);
- assert(p);
- assert(*p);
- assert(guid);
-
- while (**p != 0 && **p != ';') {
- r = parse_address_key(p, "guid", guid);
- if (r < 0)
- goto fail;
- else if (r > 0)
- continue;
-
- r = parse_address_key(p, "path", &path);
- if (r < 0)
- goto fail;
- else if (r > 0)
- continue;
-
- if (startswith(*p, "argv")) {
- unsigned ul;
-
- errno = 0;
- ul = strtoul(*p + 4, (char**) p, 10);
- if (errno > 0 || **p != '=' || ul > 256) {
- r = -EINVAL;
- goto fail;
- }
-
- (*p) ++;
-
- if (ul >= n_argv) {
- if (!GREEDY_REALLOC0(argv, allocated, ul + 2)) {
- r = -ENOMEM;
- goto fail;
- }
-
- n_argv = ul + 1;
- }
-
- r = parse_address_key(p, NULL, argv + ul);
- if (r < 0)
- goto fail;
-
- continue;
- }
-
- skip_address_key(p);
- }
-
- if (!path) {
- r = -EINVAL;
- goto fail;
- }
-
- /* Make sure there are no holes in the array, with the
- * exception of argv[0] */
- for (j = 1; j < n_argv; j++)
- if (!argv[j]) {
- r = -EINVAL;
- goto fail;
- }
-
- if (argv && argv[0] == NULL) {
- argv[0] = strdup(path);
- if (!argv[0]) {
- r = -ENOMEM;
- goto fail;
- }
- }
-
- b->exec_path = path;
- b->exec_argv = argv;
- return 0;
-
-fail:
- for (j = 0; j < n_argv; j++)
- free(argv[j]);
-
- free(argv);
- free(path);
- return r;
-}
-
-static int parse_kernel_address(sd_bus *b, const char **p, char **guid) {
- _cleanup_free_ char *path = NULL;
- int r;
-
- assert(b);
- assert(p);
- assert(*p);
- assert(guid);
-
- while (**p != 0 && **p != ';') {
- r = parse_address_key(p, "guid", guid);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- r = parse_address_key(p, "path", &path);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- skip_address_key(p);
- }
-
- if (!path)
- return -EINVAL;
-
- free(b->kernel);
- b->kernel = path;
- path = NULL;
-
- return 0;
-}
-
-static int parse_container_unix_address(sd_bus *b, const char **p, char **guid) {
- _cleanup_free_ char *machine = NULL;
- int r;
-
- assert(b);
- assert(p);
- assert(*p);
- assert(guid);
-
- while (**p != 0 && **p != ';') {
- r = parse_address_key(p, "guid", guid);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- r = parse_address_key(p, "machine", &machine);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- skip_address_key(p);
- }
-
- if (!machine)
- return -EINVAL;
-
- if (!filename_is_safe(machine))
- return -EINVAL;
-
- free(b->machine);
- b->machine = machine;
- machine = NULL;
-
- b->sockaddr.un.sun_family = AF_UNIX;
- strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path));
- b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + sizeof("/var/run/dbus/system_bus_socket") - 1;
-
- return 0;
-}
-
-static int parse_container_kernel_address(sd_bus *b, const char **p, char **guid) {
- _cleanup_free_ char *machine = NULL;
- int r;
-
- assert(b);
- assert(p);
- assert(*p);
- assert(guid);
-
- while (**p != 0 && **p != ';') {
- r = parse_address_key(p, "guid", guid);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- r = parse_address_key(p, "machine", &machine);
- if (r < 0)
- return r;
- else if (r > 0)
- continue;
-
- skip_address_key(p);
- }
-
- if (!machine)
- return -EINVAL;
-
- if (!filename_is_safe(machine))
- return -EINVAL;
-
- free(b->machine);
- b->machine = machine;
- machine = NULL;
-
- free(b->kernel);
- b->kernel = strdup("/dev/kdbus/0-system/bus");
- if (!b->kernel)
- return -ENOMEM;
-
- return 0;
-}
-
-static void bus_reset_parsed_address(sd_bus *b) {
- assert(b);
-
- zero(b->sockaddr);
- b->sockaddr_size = 0;
- strv_free(b->exec_argv);
- free(b->exec_path);
- b->exec_path = NULL;
- b->exec_argv = NULL;
- b->server_id = SD_ID128_NULL;
- free(b->kernel);
- b->kernel = NULL;
- free(b->machine);
- b->machine = NULL;
-}
-
-static int bus_parse_next_address(sd_bus *b) {
- _cleanup_free_ char *guid = NULL;
- const char *a;
- int r;
-
- assert(b);
-
- if (!b->address)
- return 0;
- if (b->address[b->address_index] == 0)
- return 0;
-
- bus_reset_parsed_address(b);
-
- a = b->address + b->address_index;
-
- while (*a != 0) {
-
- if (*a == ';') {
- a++;
- continue;
- }
-
- if (startswith(a, "unix:")) {
- a += 5;
-
- r = parse_unix_address(b, &a, &guid);
- if (r < 0)
- return r;
- break;
-
- } else if (startswith(a, "tcp:")) {
-
- a += 4;
- r = parse_tcp_address(b, &a, &guid);
- if (r < 0)
- return r;
-
- break;
-
- } else if (startswith(a, "unixexec:")) {
-
- a += 9;
- r = parse_exec_address(b, &a, &guid);
- if (r < 0)
- return r;
-
- break;
-
- } else if (startswith(a, "kernel:")) {
-
- a += 7;
- r = parse_kernel_address(b, &a, &guid);
- if (r < 0)
- return r;
-
- break;
- } else if (startswith(a, "x-container-unix:")) {
-
- a += 17;
- r = parse_container_unix_address(b, &a, &guid);
- if (r < 0)
- return r;
-
- break;
- } else if (startswith(a, "x-container-kernel:")) {
-
- a += 19;
- r = parse_container_kernel_address(b, &a, &guid);
- if (r < 0)
- return r;
-
- break;
- }
-
- a = strchr(a, ';');
- if (!a)
- return 0;
- }
-
- if (guid) {
- r = sd_id128_from_string(guid, &b->server_id);
- if (r < 0)
- return r;
- }
-
- b->address_index = a - b->address;
- return 1;
-}
-
-static int bus_start_address(sd_bus *b) {
- int r;
-
- assert(b);
-
- for (;;) {
- bool skipped = false;
-
- bus_close_fds(b);
-
- if (b->exec_path)
- r = bus_socket_exec(b);
- else if (b->machine && b->kernel)
- r = bus_container_connect_kernel(b);
- else if (b->machine && b->sockaddr.sa.sa_family != AF_UNSPEC)
- r = bus_container_connect_socket(b);
- else if (b->kernel)
- r = bus_kernel_connect(b);
- else if (b->sockaddr.sa.sa_family != AF_UNSPEC)
- r = bus_socket_connect(b);
- else
- skipped = true;
-
- if (!skipped) {
- if (r >= 0) {
- r = attach_io_events(b);
- if (r >= 0)
- return r;
- }
-
- b->last_connect_error = -r;
- }
-
- r = bus_parse_next_address(b);
- if (r < 0)
- return r;
- if (r == 0)
- return b->last_connect_error ? -b->last_connect_error : -ECONNREFUSED;
- }
-}
-
-int bus_next_address(sd_bus *b) {
- assert(b);
-
- bus_reset_parsed_address(b);
- return bus_start_address(b);
-}
-
-static int bus_start_fd(sd_bus *b) {
- struct stat st;
- int r;
-
- assert(b);
- assert(b->input_fd >= 0);
- assert(b->output_fd >= 0);
-
- r = fd_nonblock(b->input_fd, true);
- if (r < 0)
- return r;
-
- r = fd_cloexec(b->input_fd, true);
- if (r < 0)
- return r;
-
- if (b->input_fd != b->output_fd) {
- r = fd_nonblock(b->output_fd, true);
- if (r < 0)
- return r;
-
- r = fd_cloexec(b->output_fd, true);
- if (r < 0)
- return r;
- }
-
- if (fstat(b->input_fd, &st) < 0)
- return -errno;
-
- if (S_ISCHR(b->input_fd))
- return bus_kernel_take_fd(b);
- else
- return bus_socket_take_fd(b);
-}
-
-_public_ int sd_bus_start(sd_bus *bus) {
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(bus->state == BUS_UNSET, -EPERM);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- bus->state = BUS_OPENING;
-
- if (bus->is_server && bus->bus_client)
- return -EINVAL;
-
- if (bus->input_fd >= 0)
- r = bus_start_fd(bus);
- else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path || bus->kernel || bus->machine)
- r = bus_start_address(bus);
- else
- return -EINVAL;
-
- if (r < 0)
- return r;
-
- return bus_send_hello(bus);
-}
-
-_public_ int sd_bus_open_system(sd_bus **ret) {
- const char *e;
- sd_bus *b;
- int r;
-
- assert_return(ret, -EINVAL);
-
- r = sd_bus_new(&b);
- if (r < 0)
- return r;
-
- e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS");
- if (e)
- r = sd_bus_set_address(b, e);
- else
- r = sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_PATH);
- if (r < 0)
- goto fail;
-
- b->bus_client = true;
-
- /* Let's do per-method access control on the system bus. We
- * need the caller's UID and capability set for that. */
- b->trusted = false;
- b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS;
-
- r = sd_bus_start(b);
- if (r < 0)
- goto fail;
-
- *ret = b;
- return 0;
-
-fail:
- bus_free(b);
- return r;
-}
-
-_public_ int sd_bus_open_user(sd_bus **ret) {
- const char *e;
- sd_bus *b;
- int r;
-
- assert_return(ret, -EINVAL);
-
- r = sd_bus_new(&b);
- if (r < 0)
- return r;
-
- e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
- if (e) {
- r = sd_bus_set_address(b, e);
- if (r < 0)
- goto fail;
- } else {
- e = secure_getenv("XDG_RUNTIME_DIR");
- if (e) {
- _cleanup_free_ char *ee = NULL;
-
- ee = bus_address_escape(e);
- if (!ee) {
- r = -ENOMEM;
- goto fail;
- }
-
-#ifdef ENABLE_KDBUS
- asprintf(&b->address, KERNEL_USER_BUS_FMT ";" UNIX_USER_BUS_FMT, (unsigned long) getuid(), ee);
-#else
- asprintf(&b->address, UNIX_USER_BUS_FMT, ee);
-#endif
- } else {
-#ifdef ENABLE_KDBUS
- asprintf(&b->address, KERNEL_USER_BUS_FMT, (unsigned long) getuid());
-#else
- return -ECONNREFUSED;
-#endif
- }
-
- if (!b->address) {
- r = -ENOMEM;
- goto fail;
- }
- }
-
- b->bus_client = true;
-
- /* We don't do any per-method access control on the user
- * bus. */
- b->trusted = true;
-
- r = sd_bus_start(b);
- if (r < 0)
- goto fail;
-
- *ret = b;
- return 0;
-
-fail:
- bus_free(b);
- return r;
-}
-
-_public_ int sd_bus_open_system_remote(const char *host, sd_bus **ret) {
- _cleanup_free_ char *e = NULL;
- char *p = NULL;
- sd_bus *bus;
- int r;
-
- assert_return(host, -EINVAL);
- assert_return(ret, -EINVAL);
-
- e = bus_address_escape(host);
- if (!e)
- return -ENOMEM;
-
- p = strjoin("unixexec:path=ssh,argv1=-xT,argv2=", e, ",argv3=systemd-stdio-bridge", NULL);
- if (!p)
- return -ENOMEM;
-
- r = sd_bus_new(&bus);
- if (r < 0) {
- free(p);
- return r;
- }
-
- bus->address = p;
- bus->bus_client = true;
-
- r = sd_bus_start(bus);
- if (r < 0) {
- bus_free(bus);
- return r;
- }
-
- *ret = bus;
- return 0;
-}
-
-_public_ int sd_bus_open_system_container(const char *machine, sd_bus **ret) {
- _cleanup_free_ char *e = NULL;
- sd_bus *bus;
- char *p;
- int r;
-
- assert_return(machine, -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(filename_is_safe(machine), -EINVAL);
-
- e = bus_address_escape(machine);
- if (!e)
- return -ENOMEM;
-
-#ifdef ENABLE_KDBUS
- p = strjoin("x-container-kernel:machine=", e, ";x-container-unix:machine=", e, NULL);
-#else
- p = strjoin("x-container-unix:machine=", e, NULL);
-#endif
- if (!p)
- return -ENOMEM;
-
- r = sd_bus_new(&bus);
- if (r < 0) {
- free(p);
- return r;
- }
-
- bus->address = p;
- bus->bus_client = true;
-
- r = sd_bus_start(bus);
- if (r < 0) {
- bus_free(bus);
- return r;
- }
-
- *ret = bus;
- return 0;
-}
-
-_public_ void sd_bus_close(sd_bus *bus) {
-
- if (!bus)
- return;
- if (bus->state == BUS_CLOSED)
- return;
- if (bus_pid_changed(bus))
- return;
-
- bus->state = BUS_CLOSED;
-
- sd_bus_detach_event(bus);
-
- /* Drop all queued messages so that they drop references to
- * the bus object and the bus may be freed */
- bus_reset_queues(bus);
-
- if (!bus->is_kernel)
- bus_close_fds(bus);
-
- /* We'll leave the fd open in case this is a kernel bus, since
- * there might still be memblocks around that reference this
- * bus, and they might need to invoke the KDBUS_CMD_FREE
- * ioctl on the fd when they are freed. */
-}
-
-static void bus_enter_closing(sd_bus *bus) {
- assert(bus);
-
- if (bus->state != BUS_OPENING &&
- bus->state != BUS_AUTHENTICATING &&
- bus->state != BUS_HELLO &&
- bus->state != BUS_RUNNING)
- return;
-
- bus->state = BUS_CLOSING;
-}
-
-_public_ sd_bus *sd_bus_ref(sd_bus *bus) {
- assert_return(bus, NULL);
-
- assert_se(REFCNT_INC(bus->n_ref) >= 2);
-
- return bus;
-}
-
-_public_ sd_bus *sd_bus_unref(sd_bus *bus) {
- unsigned i;
-
- if (!bus)
- return NULL;
-
- i = REFCNT_DEC(bus->n_ref);
- if (i != bus->rqueue_size + bus->wqueue_size)
- return NULL;
-
- for (i = 0; i < bus->rqueue_size; i++)
- if (bus->rqueue[i]->n_ref > 1)
- return NULL;
-
- for (i = 0; i < bus->wqueue_size; i++)
- if (bus->wqueue[i]->n_ref > 1)
- return NULL;
-
- /* we are the only holders on the messages */
- bus_free(bus);
-
- return NULL;
-}
-
-_public_ int sd_bus_is_open(sd_bus *bus) {
-
- assert_return(bus, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- return BUS_IS_OPEN(bus->state);
-}
-
-_public_ int sd_bus_can_send(sd_bus *bus, char type) {
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(bus->state != BUS_UNSET, -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (type == SD_BUS_TYPE_UNIX_FD) {
- if (!(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD))
- return 0;
-
- r = bus_ensure_running(bus);
- if (r < 0)
- return r;
-
- return bus->can_fds;
- }
-
- return bus_type_is_valid(type);
-}
-
-_public_ int sd_bus_get_server_id(sd_bus *bus, sd_id128_t *server_id) {
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(server_id, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = bus_ensure_running(bus);
- if (r < 0)
- return r;
-
- *server_id = bus->server_id;
- return 0;
-}
-
-static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) {
- assert(b);
- assert(m);
-
- if (m->sealed) {
- /* If we copy the same message to multiple
- * destinations, avoid using the same cookie
- * numbers. */
- b->cookie = MAX(b->cookie, BUS_MESSAGE_COOKIE(m));
- return 0;
- }
-
- if (timeout == 0)
- timeout = BUS_DEFAULT_TIMEOUT;
-
- return bus_message_seal(m, ++b->cookie, timeout);
-}
-
-static int bus_remarshal_message(sd_bus *b, sd_bus_message **m) {
- assert(b);
-
- /* Do packet version and endianess already match? */
- if ((b->message_version == 0 || b->message_version == (*m)->header->version) &&
- (b->message_endian == 0 || b->message_endian == (*m)->header->endian))
- return 0;
-
- /* No? Then remarshal! */
- return bus_message_remarshal(b, m);
-}
-
-int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) {
- assert(b);
- assert(m);
-
- /* The bus specification says the serial number cannot be 0,
- * hence let's fill something in for synthetic messages. Since
- * synthetic messages might have a fake sender and we don't
- * want to interfere with the real sender's serial numbers we
- * pick a fixed, artifical one. We use (uint32_t) -1 rather
- * than (uint64_t) -1 since dbus1 only had 32bit identifiers,
- * even though kdbus can do 64bit. */
-
- return bus_message_seal(m, 0xFFFFFFFFULL, 0);
-}
-
-static int bus_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
- int r;
-
- assert(bus);
- assert(m);
-
- if (bus->is_kernel)
- r = bus_kernel_write_message(bus, m);
- else
- r = bus_socket_write_message(bus, m, idx);
-
- if (r <= 0)
- return r;
-
- if (bus->is_kernel || *idx >= BUS_MESSAGE_SIZE(m))
- log_debug("Sent message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%lu reply_cookie=%lu error=%s",
- bus_message_type_to_string(m->header->type),
- strna(sd_bus_message_get_sender(m)),
- strna(sd_bus_message_get_destination(m)),
- strna(sd_bus_message_get_path(m)),
- strna(sd_bus_message_get_interface(m)),
- strna(sd_bus_message_get_member(m)),
- (unsigned long) BUS_MESSAGE_COOKIE(m),
- (unsigned long) m->reply_cookie,
- strna(m->error.message));
-
- return r;
-}
-
-static int dispatch_wqueue(sd_bus *bus) {
- int r, ret = 0;
-
- assert(bus);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- while (bus->wqueue_size > 0) {
-
- r = bus_write_message(bus, bus->wqueue[0], &bus->windex);
- if (r < 0)
- return r;
- else if (r == 0)
- /* Didn't do anything this time */
- return ret;
- else if (bus->is_kernel || bus->windex >= BUS_MESSAGE_SIZE(bus->wqueue[0])) {
- /* Fully written. Let's drop the entry from
- * the queue.
- *
- * This isn't particularly optimized, but
- * well, this is supposed to be our worst-case
- * buffer only, and the socket buffer is
- * supposed to be our primary buffer, and if
- * it got full, then all bets are off
- * anyway. */
-
- bus->wqueue_size --;
- sd_bus_message_unref(bus->wqueue[0]);
- memmove(bus->wqueue, bus->wqueue + 1, sizeof(sd_bus_message*) * bus->wqueue_size);
- bus->windex = 0;
-
- ret = 1;
- }
- }
-
- return ret;
-}
-
-static int bus_read_message(sd_bus *bus) {
- assert(bus);
-
- if (bus->is_kernel)
- return bus_kernel_read_message(bus);
- else
- return bus_socket_read_message(bus);
-}
-
-int bus_rqueue_make_room(sd_bus *bus) {
- assert(bus);
-
- if (bus->rqueue_size >= BUS_RQUEUE_MAX)
- return -ENOBUFS;
-
- if (!GREEDY_REALLOC(bus->rqueue, bus->rqueue_allocated, bus->rqueue_size + 1))
- return -ENOMEM;
-
- return 0;
-}
-
-static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) {
- int r, ret = 0;
-
- assert(bus);
- assert(m);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- for (;;) {
- if (bus->rqueue_size > 0) {
- /* Dispatch a queued message */
-
- *m = bus->rqueue[0];
- bus->rqueue_size --;
- memmove(bus->rqueue, bus->rqueue + 1, sizeof(sd_bus_message*) * bus->rqueue_size);
- return 1;
- }
-
- /* Try to read a new message */
- r = bus_read_message(bus);
- if (r < 0)
- return r;
- if (r == 0)
- return ret;
-
- ret = 1;
- }
-}
-
-_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie) {
- _cleanup_bus_message_unref_ sd_bus_message *m = sd_bus_message_ref(_m);
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(m, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (m->n_fds > 0) {
- r = sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD);
- if (r < 0)
- return r;
- if (r == 0)
- return -ENOTSUP;
- }
-
- /* If the cookie number isn't kept, then we know that no reply
- * is expected */
- if (!cookie && !m->sealed)
- m->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
-
- r = bus_seal_message(bus, m, 0);
- if (r < 0)
- return r;
-
- /* Remarshall if we have to. This will possibly unref the
- * message and place a replacement in m */
- r = bus_remarshal_message(bus, &m);
- if (r < 0)
- return r;
-
- /* If this is a reply and no reply was requested, then let's
- * suppress this, if we can */
- if (m->dont_send && !cookie)
- return 1;
-
- if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) {
- size_t idx = 0;
-
- r = bus_write_message(bus, m, &idx);
- if (r < 0) {
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
- bus_enter_closing(bus);
- return -ECONNRESET;
- }
-
- return r;
- } else if (!bus->is_kernel && idx < BUS_MESSAGE_SIZE(m)) {
- /* Wasn't fully written. So let's remember how
- * much was written. Note that the first entry
- * of the wqueue array is always allocated so
- * that we always can remember how much was
- * written. */
- bus->wqueue[0] = sd_bus_message_ref(m);
- bus->wqueue_size = 1;
- bus->windex = idx;
- }
- } else {
- /* Just append it to the queue. */
-
- if (bus->wqueue_size >= BUS_WQUEUE_MAX)
- return -ENOBUFS;
-
- if (!GREEDY_REALLOC(bus->wqueue, bus->wqueue_allocated, bus->wqueue_size + 1))
- return -ENOMEM;
-
- bus->wqueue[bus->wqueue_size ++] = sd_bus_message_ref(m);
- }
-
- if (cookie)
- *cookie = BUS_MESSAGE_COOKIE(m);
-
- return 1;
-}
-
-_public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie) {
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(m, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (!streq_ptr(m->destination, destination)) {
-
- if (!destination)
- return -EEXIST;
-
- r = sd_bus_message_set_destination(m, destination);
- if (r < 0)
- return r;
- }
-
- return sd_bus_send(bus, m, cookie);
-}
-
-static usec_t calc_elapse(uint64_t usec) {
- if (usec == (uint64_t) -1)
- return 0;
-
- return now(CLOCK_MONOTONIC) + usec;
-}
-
-static int timeout_compare(const void *a, const void *b) {
- const struct reply_callback *x = a, *y = b;
-
- if (x->timeout != 0 && y->timeout == 0)
- return -1;
-
- if (x->timeout == 0 && y->timeout != 0)
- return 1;
-
- if (x->timeout < y->timeout)
- return -1;
-
- if (x->timeout > y->timeout)
- return 1;
-
- return 0;
-}
-
-_public_ int sd_bus_call_async(
- sd_bus *bus,
- sd_bus_message *_m,
- sd_bus_message_handler_t callback,
- void *userdata,
- uint64_t usec,
- uint64_t *cookie) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = sd_bus_message_ref(_m);
- struct reply_callback *c;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(m, -EINVAL);
- assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
- assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = hashmap_ensure_allocated(&bus->reply_callbacks, uint64_hash_func, uint64_compare_func);
- if (r < 0)
- return r;
-
- r = prioq_ensure_allocated(&bus->reply_callbacks_prioq, timeout_compare);
- if (r < 0)
- return r;
-
- r = bus_seal_message(bus, m, usec);
- if (r < 0)
- return r;
-
- r = bus_remarshal_message(bus, &m);
- if (r < 0)
- return r;
-
- c = new0(struct reply_callback, 1);
- if (!c)
- return -ENOMEM;
-
- c->callback = callback;
- c->userdata = userdata;
- c->cookie = BUS_MESSAGE_COOKIE(m);
- c->timeout = calc_elapse(m->timeout);
-
- r = hashmap_put(bus->reply_callbacks, &c->cookie, c);
- if (r < 0) {
- free(c);
- return r;
- }
-
- if (c->timeout != 0) {
- r = prioq_put(bus->reply_callbacks_prioq, c, &c->prioq_idx);
- if (r < 0) {
- c->timeout = 0;
- sd_bus_call_async_cancel(bus, c->cookie);
- return r;
- }
- }
-
- r = sd_bus_send(bus, m, cookie);
- if (r < 0) {
- sd_bus_call_async_cancel(bus, c->cookie);
- return r;
- }
-
- return r;
-}
-
-_public_ int sd_bus_call_async_cancel(sd_bus *bus, uint64_t cookie) {
- struct reply_callback *c;
-
- assert_return(bus, -EINVAL);
- assert_return(cookie != 0, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- c = hashmap_remove(bus->reply_callbacks, &cookie);
- if (!c)
- return 0;
-
- if (c->timeout != 0)
- prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
-
- free(c);
- return 1;
-}
-
-int bus_ensure_running(sd_bus *bus) {
- int r;
-
- assert(bus);
-
- if (bus->state == BUS_UNSET || bus->state == BUS_CLOSED || bus->state == BUS_CLOSING)
- return -ENOTCONN;
- if (bus->state == BUS_RUNNING)
- return 1;
-
- for (;;) {
- r = sd_bus_process(bus, NULL);
- if (r < 0)
- return r;
- if (bus->state == BUS_RUNNING)
- return 1;
- if (r > 0)
- continue;
-
- r = sd_bus_wait(bus, (uint64_t) -1);
- if (r < 0)
- return r;
- }
-}
-
-_public_ int sd_bus_call(
- sd_bus *bus,
- sd_bus_message *_m,
- uint64_t usec,
- sd_bus_error *error,
- sd_bus_message **reply) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = sd_bus_message_ref(_m);
- usec_t timeout;
- uint64_t cookie;
- unsigned i;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(m, -EINVAL);
- assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
- assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
- assert_return(!bus_error_is_dirty(error), -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = bus_ensure_running(bus);
- if (r < 0)
- return r;
-
- i = bus->rqueue_size;
-
- r = bus_seal_message(bus, m, usec);
- if (r < 0)
- return r;
-
- r = bus_remarshal_message(bus, &m);
- if (r < 0)
- return r;
-
- r = sd_bus_send(bus, m, &cookie);
- if (r < 0)
- return r;
-
- timeout = calc_elapse(m->timeout);
-
- for (;;) {
- usec_t left;
-
- while (i < bus->rqueue_size) {
- sd_bus_message *incoming = NULL;
-
- incoming = bus->rqueue[i];
-
- if (incoming->reply_cookie == cookie) {
- /* Found a match! */
-
- memmove(bus->rqueue + i, bus->rqueue + i + 1, sizeof(sd_bus_message*) * (bus->rqueue_size - i - 1));
- bus->rqueue_size--;
-
- if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) {
-
- if (reply)
- *reply = incoming;
- else
- sd_bus_message_unref(incoming);
-
- return 1;
- } else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR)
- r = sd_bus_error_copy(error, &incoming->error);
- else
- r = -EIO;
-
- sd_bus_message_unref(incoming);
- return r;
-
- } else if (BUS_MESSAGE_COOKIE(incoming) == cookie &&
- bus->unique_name &&
- incoming->sender &&
- streq(bus->unique_name, incoming->sender)) {
-
- memmove(bus->rqueue + i, bus->rqueue + i + 1, sizeof(sd_bus_message*) * (bus->rqueue_size - i - 1));
- bus->rqueue_size--;
-
- /* Our own message? Somebody is trying
- * to send its own client a message,
- * let's not dead-lock, let's fail
- * immediately. */
-
- sd_bus_message_unref(incoming);
- return -ELOOP;
- }
-
- /* Try to read more, right-away */
- i++;
- }
-
- r = bus_read_message(bus);
- if (r < 0) {
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
- bus_enter_closing(bus);
- return -ECONNRESET;
- }
-
- return r;
- }
- if (r > 0)
- continue;
-
- if (timeout > 0) {
- usec_t n;
-
- n = now(CLOCK_MONOTONIC);
- if (n >= timeout)
- return -ETIMEDOUT;
-
- left = timeout - n;
- } else
- left = (uint64_t) -1;
-
- r = bus_poll(bus, true, left);
- if (r < 0)
- return r;
- if (r == 0)
- return -ETIMEDOUT;
-
- r = dispatch_wqueue(bus);
- if (r < 0) {
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
- bus_enter_closing(bus);
- return -ECONNRESET;
- }
-
- return r;
- }
- }
-}
-
-_public_ int sd_bus_get_fd(sd_bus *bus) {
-
- assert_return(bus, -EINVAL);
- assert_return(bus->input_fd == bus->output_fd, -EPERM);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- return bus->input_fd;
-}
-
-_public_ int sd_bus_get_events(sd_bus *bus) {
- int flags = 0;
-
- assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state) || bus->state == BUS_CLOSING, -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (bus->state == BUS_OPENING)
- flags |= POLLOUT;
- else if (bus->state == BUS_AUTHENTICATING) {
-
- if (bus_socket_auth_needs_write(bus))
- flags |= POLLOUT;
-
- flags |= POLLIN;
-
- } else if (bus->state == BUS_RUNNING || bus->state == BUS_HELLO) {
- if (bus->rqueue_size <= 0)
- flags |= POLLIN;
- if (bus->wqueue_size > 0)
- flags |= POLLOUT;
- }
-
- return flags;
-}
-
-_public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
- struct reply_callback *c;
-
- assert_return(bus, -EINVAL);
- assert_return(timeout_usec, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state) || bus->state == BUS_CLOSING, -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (bus->state == BUS_CLOSING) {
- *timeout_usec = 0;
- return 1;
- }
-
- if (bus->state == BUS_AUTHENTICATING) {
- *timeout_usec = bus->auth_timeout;
- return 1;
- }
-
- if (bus->state != BUS_RUNNING && bus->state != BUS_HELLO) {
- *timeout_usec = (uint64_t) -1;
- return 0;
- }
-
- if (bus->rqueue_size > 0) {
- *timeout_usec = 0;
- return 1;
- }
-
- c = prioq_peek(bus->reply_callbacks_prioq);
- if (!c) {
- *timeout_usec = (uint64_t) -1;
- return 0;
- }
-
- *timeout_usec = c->timeout;
- return 1;
-}
-
-static int process_timeout(sd_bus *bus) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message* m = NULL;
- struct reply_callback *c;
- usec_t n;
- int r;
-
- assert(bus);
-
- c = prioq_peek(bus->reply_callbacks_prioq);
- if (!c)
- return 0;
-
- n = now(CLOCK_MONOTONIC);
- if (c->timeout > n)
- return 0;
-
- r = bus_message_new_synthetic_error(
- bus,
- c->cookie,
- &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call timed out"),
- &m);
- if (r < 0)
- return r;
-
- m->sender = "org.freedesktop.DBus";
-
- r = bus_seal_synthetic_message(bus, m);
- if (r < 0)
- return r;
-
- assert_se(prioq_pop(bus->reply_callbacks_prioq) == c);
- hashmap_remove(bus->reply_callbacks, &c->cookie);
-
- bus->current = m;
- bus->iteration_counter ++;
-
- r = c->callback(bus, m, c->userdata, &error_buffer);
- r = bus_maybe_reply_error(m, r, &error_buffer);
- free(c);
-
- bus->current = NULL;
-
- return r;
-}
-
-static int process_hello(sd_bus *bus, sd_bus_message *m) {
- assert(bus);
- assert(m);
-
- if (bus->state != BUS_HELLO)
- return 0;
-
- /* Let's make sure the first message on the bus is the HELLO
- * reply. But note that we don't actually parse the message
- * here (we leave that to the usual handling), we just verify
- * we don't let any earlier msg through. */
-
- if (m->header->type != SD_BUS_MESSAGE_METHOD_RETURN &&
- m->header->type != SD_BUS_MESSAGE_METHOD_ERROR)
- return -EIO;
-
- if (m->reply_cookie != bus->hello_cookie)
- return -EIO;
-
- return 0;
-}
-
-static int process_reply(sd_bus *bus, sd_bus_message *m) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
- struct reply_callback *c;
- int r;
-
- assert(bus);
- assert(m);
-
- if (m->header->type != SD_BUS_MESSAGE_METHOD_RETURN &&
- m->header->type != SD_BUS_MESSAGE_METHOD_ERROR)
- return 0;
-
- c = hashmap_remove(bus->reply_callbacks, &m->reply_cookie);
- if (!c)
- return 0;
-
- if (c->timeout != 0)
- prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- r = c->callback(bus, m, c->userdata, &error_buffer);
- r = bus_maybe_reply_error(m, r, &error_buffer);
- free(c);
-
- return r;
-}
-
-static int process_filter(sd_bus *bus, sd_bus_message *m) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
- struct filter_callback *l;
- int r;
-
- assert(bus);
- assert(m);
-
- do {
- bus->filter_callbacks_modified = false;
-
- LIST_FOREACH(callbacks, l, bus->filter_callbacks) {
-
- if (bus->filter_callbacks_modified)
- break;
-
- /* Don't run this more than once per iteration */
- if (l->last_iteration == bus->iteration_counter)
- continue;
-
- l->last_iteration = bus->iteration_counter;
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- r = l->callback(bus, m, l->userdata, &error_buffer);
- r = bus_maybe_reply_error(m, r, &error_buffer);
- if (r != 0)
- return r;
-
- }
-
- } while (bus->filter_callbacks_modified);
-
- return 0;
-}
-
-static int process_match(sd_bus *bus, sd_bus_message *m) {
- int r;
-
- assert(bus);
- assert(m);
-
- do {
- bus->match_callbacks_modified = false;
-
- r = bus_match_run(bus, &bus->match_callbacks, m);
- if (r != 0)
- return r;
-
- } while (bus->match_callbacks_modified);
-
- return 0;
-}
-
-static int process_builtin(sd_bus *bus, sd_bus_message *m) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- int r;
-
- assert(bus);
- assert(m);
-
- if (bus->manual_peer_interface)
- return 0;
-
- if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
- return 0;
-
- if (!streq_ptr(m->interface, "org.freedesktop.DBus.Peer"))
- return 0;
-
- if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
- return 1;
-
- if (streq_ptr(m->member, "Ping"))
- r = sd_bus_message_new_method_return(m, &reply);
- else if (streq_ptr(m->member, "GetMachineId")) {
- sd_id128_t id;
- char sid[33];
-
- r = sd_id128_get_machine(&id);
- if (r < 0)
- return r;
-
- r = sd_bus_message_new_method_return(m, &reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(reply, "s", sd_id128_to_string(id, sid));
- } else {
- r = sd_bus_message_new_method_errorf(
- m, &reply,
- SD_BUS_ERROR_UNKNOWN_METHOD,
- "Unknown method '%s' on interface '%s'.", m->member, m->interface);
- }
-
- if (r < 0)
- return r;
-
- r = sd_bus_send(bus, reply, NULL);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int process_message(sd_bus *bus, sd_bus_message *m) {
- int r;
-
- assert(bus);
- assert(m);
-
- bus->current = m;
- bus->iteration_counter++;
-
- log_debug("Got message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%lu reply_cookie=%lu error=%s",
- bus_message_type_to_string(m->header->type),
- strna(sd_bus_message_get_sender(m)),
- strna(sd_bus_message_get_destination(m)),
- strna(sd_bus_message_get_path(m)),
- strna(sd_bus_message_get_interface(m)),
- strna(sd_bus_message_get_member(m)),
- (unsigned long) BUS_MESSAGE_COOKIE(m),
- (unsigned long) m->reply_cookie,
- strna(m->error.message));
-
- r = process_hello(bus, m);
- if (r != 0)
- goto finish;
-
- r = process_reply(bus, m);
- if (r != 0)
- goto finish;
-
- r = process_filter(bus, m);
- if (r != 0)
- goto finish;
-
- r = process_match(bus, m);
- if (r != 0)
- goto finish;
-
- r = process_builtin(bus, m);
- if (r != 0)
- goto finish;
-
- r = bus_process_object(bus, m);
-
-finish:
- bus->current = NULL;
- return r;
-}
-
-static int process_running(sd_bus *bus, sd_bus_message **ret) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert(bus);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- r = process_timeout(bus);
- if (r != 0)
- goto null_message;
-
- r = dispatch_wqueue(bus);
- if (r != 0)
- goto null_message;
-
- r = dispatch_rqueue(bus, &m);
- if (r < 0)
- return r;
- if (!m)
- goto null_message;
-
- r = process_message(bus, m);
- if (r != 0)
- goto null_message;
-
- if (ret) {
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- *ret = m;
- m = NULL;
- return 1;
- }
-
- if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) {
-
- log_debug("Unprocessed message call sender=%s object=%s interface=%s member=%s",
- strna(sd_bus_message_get_sender(m)),
- strna(sd_bus_message_get_path(m)),
- strna(sd_bus_message_get_interface(m)),
- strna(sd_bus_message_get_member(m)));
-
- r = sd_bus_reply_method_errorf(
- m,
- SD_BUS_ERROR_UNKNOWN_OBJECT,
- "Unknown object '%s'.", m->path);
- if (r < 0)
- return r;
- }
-
- return 1;
-
-null_message:
- if (r >= 0 && ret)
- *ret = NULL;
-
- return r;
-}
-
-static int process_closing(sd_bus *bus, sd_bus_message **ret) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- struct reply_callback *c;
- int r;
-
- assert(bus);
- assert(bus->state == BUS_CLOSING);
-
- c = hashmap_first(bus->reply_callbacks);
- if (c) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
-
- /* First, fail all outstanding method calls */
- r = bus_message_new_synthetic_error(
- bus,
- c->cookie,
- &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Connection terminated"),
- &m);
- if (r < 0)
- return r;
-
- r = bus_seal_synthetic_message(bus, m);
- if (r < 0)
- return r;
-
- if (c->timeout != 0)
- prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
-
- hashmap_remove(bus->reply_callbacks, &c->cookie);
-
- bus->current = m;
- bus->iteration_counter++;
-
- r = c->callback(bus, m, c->userdata, &error_buffer);
- r = bus_maybe_reply_error(m, r, &error_buffer);
- free(c);
-
- goto finish;
- }
-
- /* Then, synthesize a Disconnected message */
- r = sd_bus_message_new_signal(
- bus,
- "/org/freedesktop/DBus/Local",
- "org.freedesktop.DBus.Local",
- "Disconnected",
- &m);
- if (r < 0)
- return r;
-
- m->sender = "org.freedesktop.DBus.Local";
-
- r = bus_seal_synthetic_message(bus, m);
- if (r < 0)
- return r;
-
- sd_bus_close(bus);
-
- bus->current = m;
- bus->iteration_counter++;
-
- r = process_filter(bus, m);
- if (r != 0)
- goto finish;
-
- r = process_match(bus, m);
- if (r != 0)
- goto finish;
-
- if (ret) {
- *ret = m;
- m = NULL;
- }
-
- r = 1;
-
-finish:
- bus->current = NULL;
- return r;
-}
-
-_public_ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
- BUS_DONT_DESTROY(bus);
- int r;
-
- /* Returns 0 when we didn't do anything. This should cause the
- * caller to invoke sd_bus_wait() before returning the next
- * time. Returns > 0 when we did something, which possibly
- * means *ret is filled in with an unprocessed message. */
-
- assert_return(bus, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- /* We don't allow recursively invoking sd_bus_process(). */
- assert_return(!bus->current, -EBUSY);
-
- switch (bus->state) {
-
- case BUS_UNSET:
- return -ENOTCONN;
-
- case BUS_CLOSED:
- return -ECONNRESET;
-
- case BUS_OPENING:
- r = bus_socket_process_opening(bus);
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
- bus_enter_closing(bus);
- r = 1;
- } else if (r < 0)
- return r;
- if (ret)
- *ret = NULL;
- return r;
-
- case BUS_AUTHENTICATING:
- r = bus_socket_process_authenticating(bus);
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
- bus_enter_closing(bus);
- r = 1;
- } else if (r < 0)
- return r;
-
- if (ret)
- *ret = NULL;
-
- return r;
-
- case BUS_RUNNING:
- case BUS_HELLO:
- r = process_running(bus, ret);
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
- bus_enter_closing(bus);
- r = 1;
-
- if (ret)
- *ret = NULL;
- }
-
- return r;
-
- case BUS_CLOSING:
- return process_closing(bus, ret);
- }
-
- assert_not_reached("Unknown state");
-}
-
-static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
- struct pollfd p[2] = {};
- int r, e, n;
- struct timespec ts;
- usec_t m = (usec_t) -1;
-
- assert(bus);
-
- if (bus->state == BUS_CLOSING)
- return 1;
-
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
-
- e = sd_bus_get_events(bus);
- if (e < 0)
- return e;
-
- if (need_more)
- /* The caller really needs some more data, he doesn't
- * care about what's already read, or any timeouts
- * except its own.*/
- e |= POLLIN;
- else {
- usec_t until;
- /* The caller wants to process if there's something to
- * process, but doesn't care otherwise */
-
- r = sd_bus_get_timeout(bus, &until);
- if (r < 0)
- return r;
- if (r > 0) {
- usec_t nw;
- nw = now(CLOCK_MONOTONIC);
- m = until > nw ? until - nw : 0;
- }
- }
-
- if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
- m = timeout_usec;
-
- p[0].fd = bus->input_fd;
- if (bus->output_fd == bus->input_fd) {
- p[0].events = e;
- n = 1;
- } else {
- p[0].events = e & POLLIN;
- p[1].fd = bus->output_fd;
- p[1].events = e & POLLOUT;
- n = 2;
- }
-
- r = ppoll(p, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
- if (r < 0)
- return -errno;
-
- return r > 0 ? 1 : 0;
-}
-
-_public_ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
-
- assert_return(bus, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (bus->state == BUS_CLOSING)
- return 0;
-
- assert_return(BUS_IS_OPEN(bus->state) , -ENOTCONN);
-
- if (bus->rqueue_size > 0)
- return 0;
-
- return bus_poll(bus, false, timeout_usec);
-}
-
-_public_ int sd_bus_flush(sd_bus *bus) {
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (bus->state == BUS_CLOSING)
- return 0;
-
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
-
- r = bus_ensure_running(bus);
- if (r < 0)
- return r;
-
- if (bus->wqueue_size <= 0)
- return 0;
-
- for (;;) {
- r = dispatch_wqueue(bus);
- if (r < 0) {
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
- bus_enter_closing(bus);
- return -ECONNRESET;
- }
-
- return r;
- }
-
- if (bus->wqueue_size <= 0)
- return 0;
-
- r = bus_poll(bus, false, (uint64_t) -1);
- if (r < 0)
- return r;
- }
-}
-
-_public_ int sd_bus_add_filter(sd_bus *bus,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- struct filter_callback *f;
-
- assert_return(bus, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- f = new0(struct filter_callback, 1);
- if (!f)
- return -ENOMEM;
- f->callback = callback;
- f->userdata = userdata;
-
- bus->filter_callbacks_modified = true;
- LIST_PREPEND(callbacks, bus->filter_callbacks, f);
- return 0;
-}
-
-_public_ int sd_bus_remove_filter(sd_bus *bus,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- struct filter_callback *f;
-
- assert_return(bus, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- LIST_FOREACH(callbacks, f, bus->filter_callbacks) {
- if (f->callback == callback && f->userdata == userdata) {
- bus->filter_callbacks_modified = true;
- LIST_REMOVE(callbacks, bus->filter_callbacks, f);
- free(f);
- return 1;
- }
- }
-
- return 0;
-}
-
-_public_ int sd_bus_add_match(sd_bus *bus,
- const char *match,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- struct bus_match_component *components = NULL;
- unsigned n_components = 0;
- uint64_t cookie = 0;
- int r = 0;
-
- assert_return(bus, -EINVAL);
- assert_return(match, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = bus_match_parse(match, &components, &n_components);
- if (r < 0)
- goto finish;
-
- if (bus->bus_client) {
- cookie = ++bus->match_cookie;
-
- r = bus_add_match_internal(bus, match, components, n_components, cookie);
- if (r < 0)
- goto finish;
- }
-
- bus->match_callbacks_modified = true;
- r = bus_match_add(&bus->match_callbacks, components, n_components, callback, userdata, cookie, NULL);
- if (r < 0) {
- if (bus->bus_client)
- bus_remove_match_internal(bus, match, cookie);
- }
-
-finish:
- bus_match_parse_free(components, n_components);
- return r;
-}
-
-_public_ int sd_bus_remove_match(sd_bus *bus,
- const char *match,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- struct bus_match_component *components = NULL;
- unsigned n_components = 0;
- int r = 0, q = 0;
- uint64_t cookie = 0;
-
- assert_return(bus, -EINVAL);
- assert_return(match, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = bus_match_parse(match, &components, &n_components);
- if (r < 0)
- return r;
-
- bus->match_callbacks_modified = true;
- r = bus_match_remove(&bus->match_callbacks, components, n_components, callback, userdata, &cookie);
-
- if (bus->bus_client)
- q = bus_remove_match_internal(bus, match, cookie);
-
- bus_match_parse_free(components, n_components);
-
- return r < 0 ? r : q;
-}
-
-bool bus_pid_changed(sd_bus *bus) {
- assert(bus);
-
- /* We don't support people creating a bus connection and
- * keeping it around over a fork(). Let's complain. */
-
- return bus->original_pid != getpid();
-}
-
-static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- sd_bus *bus = userdata;
- int r;
-
- assert(bus);
-
- r = sd_bus_process(bus, NULL);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_bus *bus = userdata;
- int r;
-
- assert(bus);
-
- r = sd_bus_process(bus, NULL);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int prepare_callback(sd_event_source *s, void *userdata) {
- sd_bus *bus = userdata;
- int r, e;
- usec_t until;
-
- assert(s);
- assert(bus);
-
- e = sd_bus_get_events(bus);
- if (e < 0)
- return e;
-
- if (bus->output_fd != bus->input_fd) {
-
- r = sd_event_source_set_io_events(bus->input_io_event_source, e & POLLIN);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_io_events(bus->output_io_event_source, e & POLLOUT);
- if (r < 0)
- return r;
- } else {
- r = sd_event_source_set_io_events(bus->input_io_event_source, e);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_get_timeout(bus, &until);
- if (r < 0)
- return r;
- if (r > 0) {
- int j;
-
- j = sd_event_source_set_time(bus->time_event_source, until);
- if (j < 0)
- return j;
- }
-
- r = sd_event_source_set_enabled(bus->time_event_source, r > 0);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int quit_callback(sd_event_source *event, void *userdata) {
- sd_bus *bus = userdata;
-
- assert(event);
-
- sd_bus_flush(bus);
-
- return 1;
-}
-
-static int attach_io_events(sd_bus *bus) {
- int r;
-
- assert(bus);
-
- if (bus->input_fd < 0)
- return 0;
-
- if (!bus->event)
- return 0;
-
- if (!bus->input_io_event_source) {
- r = sd_event_add_io(bus->event, bus->input_fd, 0, io_callback, bus, &bus->input_io_event_source);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_prepare(bus->input_io_event_source, prepare_callback);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(bus->input_io_event_source, bus->event_priority);
- } else
- r = sd_event_source_set_io_fd(bus->input_io_event_source, bus->input_fd);
-
- if (r < 0)
- return r;
-
- if (bus->output_fd != bus->input_fd) {
- assert(bus->output_fd >= 0);
-
- if (!bus->output_io_event_source) {
- r = sd_event_add_io(bus->event, bus->output_fd, 0, io_callback, bus, &bus->output_io_event_source);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(bus->output_io_event_source, bus->event_priority);
- } else
- r = sd_event_source_set_io_fd(bus->output_io_event_source, bus->output_fd);
-
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static void detach_io_events(sd_bus *bus) {
- assert(bus);
-
- if (bus->input_io_event_source) {
- sd_event_source_set_enabled(bus->input_io_event_source, SD_EVENT_OFF);
- bus->input_io_event_source = sd_event_source_unref(bus->input_io_event_source);
- }
-
- if (bus->output_io_event_source) {
- sd_event_source_set_enabled(bus->output_io_event_source, SD_EVENT_OFF);
- bus->output_io_event_source = sd_event_source_unref(bus->output_io_event_source);
- }
-}
-
-_public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) {
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(!bus->event, -EBUSY);
-
- assert(!bus->input_io_event_source);
- assert(!bus->output_io_event_source);
- assert(!bus->time_event_source);
-
- if (event)
- bus->event = sd_event_ref(event);
- else {
- r = sd_event_default(&bus->event);
- if (r < 0)
- return r;
- }
-
- bus->event_priority = priority;
-
- r = sd_event_add_monotonic(bus->event, 0, 0, time_callback, bus, &bus->time_event_source);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_priority(bus->time_event_source, priority);
- if (r < 0)
- goto fail;
-
- r = sd_event_add_exit(bus->event, quit_callback, bus, &bus->quit_event_source);
- if (r < 0)
- goto fail;
-
- r = attach_io_events(bus);
- if (r < 0)
- goto fail;
-
- return 0;
-
-fail:
- sd_bus_detach_event(bus);
- return r;
-}
-
-_public_ int sd_bus_detach_event(sd_bus *bus) {
- assert_return(bus, -EINVAL);
-
- if (!bus->event)
- return 0;
-
- detach_io_events(bus);
-
- if (bus->time_event_source) {
- sd_event_source_set_enabled(bus->time_event_source, SD_EVENT_OFF);
- bus->time_event_source = sd_event_source_unref(bus->time_event_source);
- }
-
- if (bus->quit_event_source) {
- sd_event_source_set_enabled(bus->quit_event_source, SD_EVENT_OFF);
- bus->quit_event_source = sd_event_source_unref(bus->quit_event_source);
- }
-
- if (bus->event)
- bus->event = sd_event_unref(bus->event);
-
- return 1;
-}
-
-_public_ sd_event* sd_bus_get_event(sd_bus *bus) {
- assert_return(bus, NULL);
-
- return bus->event;
-}
-
-_public_ sd_bus_message* sd_bus_get_current(sd_bus *bus) {
- assert_return(bus, NULL);
-
- return bus->current;
-}
-
-static int bus_default(int (*bus_open)(sd_bus **), sd_bus **default_bus, sd_bus **ret) {
- sd_bus *b = NULL;
- int r;
-
- assert(bus_open);
- assert(default_bus);
-
- if (!ret)
- return !!*default_bus;
-
- if (*default_bus) {
- *ret = sd_bus_ref(*default_bus);
- return 0;
- }
-
- r = bus_open(&b);
- if (r < 0)
- return r;
-
- b->default_bus_ptr = default_bus;
- b->tid = gettid();
- *default_bus = b;
-
- *ret = b;
- return 1;
-}
-
-_public_ int sd_bus_default_system(sd_bus **ret) {
- static thread_local sd_bus *default_system_bus = NULL;
-
- return bus_default(sd_bus_open_system, &default_system_bus, ret);
-}
-
-_public_ int sd_bus_default_user(sd_bus **ret) {
- static thread_local sd_bus *default_user_bus = NULL;
-
- return bus_default(sd_bus_open_user, &default_user_bus, ret);
-}
-
-_public_ int sd_bus_get_tid(sd_bus *b, pid_t *tid) {
- assert_return(b, -EINVAL);
- assert_return(tid, -EINVAL);
- assert_return(!bus_pid_changed(b), -ECHILD);
-
- if (b->tid != 0) {
- *tid = b->tid;
- return 0;
- }
-
- if (b->event)
- return sd_event_get_tid(b->event, tid);
-
- return -ENXIO;
-}
-
-_public_ char *sd_bus_label_escape(const char *s) {
- char *r, *t;
- const char *f;
-
- assert_return(s, NULL);
-
- /* Escapes all chars that D-Bus' object path cannot deal
- * with. Can be reversed with bus_path_unescape(). We special
- * case the empty string. */
-
- if (*s == 0)
- return strdup("_");
-
- r = new(char, strlen(s)*3 + 1);
- if (!r)
- return NULL;
-
- for (f = s, t = r; *f; f++) {
-
- /* Escape everything that is not a-zA-Z0-9. We also
- * escape 0-9 if it's the first character */
-
- if (!(*f >= 'A' && *f <= 'Z') &&
- !(*f >= 'a' && *f <= 'z') &&
- !(f > s && *f >= '0' && *f <= '9')) {
- *(t++) = '_';
- *(t++) = hexchar(*f >> 4);
- *(t++) = hexchar(*f);
- } else
- *(t++) = *f;
- }
-
- *t = 0;
-
- return r;
-}
-
-_public_ char *sd_bus_label_unescape(const char *f) {
- char *r, *t;
-
- assert_return(f, NULL);
-
- /* Special case for the empty string */
- if (streq(f, "_"))
- return strdup("");
-
- r = new(char, strlen(f) + 1);
- if (!r)
- return NULL;
-
- for (t = r; *f; f++) {
-
- if (*f == '_') {
- int a, b;
-
- if ((a = unhexchar(f[1])) < 0 ||
- (b = unhexchar(f[2])) < 0) {
- /* Invalid escape code, let's take it literal then */
- *(t++) = '_';
- } else {
- *(t++) = (char) ((a << 4) | b);
- f += 2;
- }
- } else
- *(t++) = *f;
- }
-
- *t = 0;
-
- return r;
-}
-
-_public_ int sd_bus_get_peer_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
- sd_bus_creds *c;
- pid_t pid = 0;
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP);
- assert_return(ret, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(!bus->is_kernel, -ENOTSUP);
-
- if (!bus->ucred_valid && !isempty(bus->label))
- return -ENODATA;
-
- c = bus_creds_new();
- if (!c)
- return -ENOMEM;
-
- if (bus->ucred_valid) {
- pid = c->pid = bus->ucred.pid;
- c->uid = bus->ucred.uid;
- c->gid = bus->ucred.gid;
-
- c->mask |= (SD_BUS_CREDS_UID | SD_BUS_CREDS_PID | SD_BUS_CREDS_GID) & mask;
- }
-
- if (!isempty(bus->label) && (mask & SD_BUS_CREDS_SELINUX_CONTEXT)) {
- c->label = strdup(bus->label);
- if (!c->label) {
- sd_bus_creds_unref(c);
- return -ENOMEM;
- }
-
- c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
- }
-
- r = bus_creds_add_more(c, mask, pid, 0);
- if (r < 0)
- return r;
-
- *ret = c;
- return 0;
-}
-
-_public_ int sd_bus_try_close(sd_bus *bus) {
- int r;
-
- assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
- assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(bus->is_kernel, -ENOTSUP);
-
- if (bus->rqueue_size > 0)
- return -EBUSY;
-
- if (bus->wqueue_size > 0)
- return -EBUSY;
-
- r = bus_kernel_try_close(bus);
- if (r < 0)
- return r;
-
- sd_bus_close(bus);
- return 0;
-}
diff --git a/src/libsystemd/sd-bus/.gitignore b/src/libsystemd/sd-bus/.gitignore
new file mode 100644
index 0000000..d32542e
--- /dev/null
+++ b/src/libsystemd/sd-bus/.gitignore
@@ -0,0 +1 @@
+/bus-error-mapping.c
diff --git a/src/libsystemd/sd-bus/DIFFERENCES b/src/libsystemd/sd-bus/DIFFERENCES
new file mode 100644
index 0000000..fd7506b
--- /dev/null
+++ b/src/libsystemd/sd-bus/DIFFERENCES
@@ -0,0 +1,28 @@
+Known differences between dbus1 and kdbus:
+
+- NameAcquired/NameLost is gone entirely on kdbus backends if
+ libsystemd is used. It is still added in by systemd-bus-proxyd
+ for old dbus1 clients, and it is available if libsystemd is used
+ against the classic dbus1 daemon. If you want to write compatible
+ code with libsystem-bus you need to explicitly subscribe to
+ NameOwnerChanged signals and just ignore NameAcquired/NameLost
+
+- Applications have to deal with spurious signals they didn't expect,
+ due to the probabilistic bloom filters. They need to handle this
+ anyway, given that any client can send anything to arbitrary clients
+ anyway, even in dbus1, so not much changes.
+
+- clients of the system bus when kdbus is used must roll their own
+ security. Only legacy dbus1 clients get the old XML policy enforced,
+ which is implemented by systemd-bus-proxyd.
+
+- Serial numbers of synthesized messages are always (uint32_t) -1.
+
+- The org.freedesktop.DBus "driver" service is not special on
+ kdbus. It is a bus activated service like any other with its own
+ unique name.
+
+- NameOwnerChanged is a synthetic message, generated locally and not
+ by the driver.
+
+- There's no standard per-session bus anymore. Only a per-user bus.
diff --git a/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION b/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION
new file mode 100644
index 0000000..5dffc25
--- /dev/null
+++ b/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION
@@ -0,0 +1,63 @@
+How we use GVariant for serializing D-Bus messages
+--------------------------------------------------
+
+We stay as close to the original dbus1 framing as possible. dbus1 has
+the following framing:
+
+ 1. A fixed header of "yyyyuu"
+ 2. Additional header fields of "a(yv)"
+ 3. Padding with NUL bytes to pad up to next 8byte boundary
+ 4. The body
+
+Note that the body is not padded at the end, the complete message
+hence might have a non-aligned size. Reading multiple messages at once
+will hence result in possibly unaligned messages in memory.
+
+The header consists of the following:
+
+ y Endianness, 'l' or 'B'
+ y Message Type
+ y Flags
+ y Protocol version, '1'
+ u Length of the body, i.e. the length of part 4 above
+ u Serial number
+
+ = 12 bytes
+
+When using GVariant we keep the basic structure in place, only
+slightly extend the header, and define protocol version '2'. The new
+header:
+
+ y Endianness, 'l' or 'B'
+ y Message Type
+ y Flags
+ y Protocol version, '2'
+ u Length of the body, i.e. the length of part 4 above
+ u Serial number
+ u Length of the additional header fields array
+
+ = 16 bytes
+
+This has the nice benefit that the beginning of the additional header
+fields array is aligned to an 8 byte boundary. Also, in dbus1
+marshalling arrays start with a length value of 32bit, which means in
+both dbus1 and gvariant marshallings the size of the header fields
+array will be at the same location between bytes 12 and 16. To
+visualize that:
+
+ 0 4 8 12 16
+ Common: | E | T | F | V | Body Length | Serial | Fields Length |
+
+ dbus1: | ... (as above) ... | Fields array ...
+
+ gvariant: | ... (as above) ... | Fields Length | Fields array ...
+
+And that's already it.
+
+Note: on kdbus only native endian messages marshalled in gvariant may
+ be sent. If a client receives a message in non-native endianness
+ or in dbus1 marshalling it shall ignore the message.
+
+Note: The GVariant "MAYBE" type is not supported, so that messages can
+ be fully converted forth and back between dbus1 and gvariant
+ representations.
diff --git a/src/libsystemd/sd-bus/Makefile b/src/libsystemd/sd-bus/Makefile
new file mode 120000
index 0000000..94aaae2
--- /dev/null
+++ b/src/libsystemd/sd-bus/Makefile
@@ -0,0 +1 @@
+../../Makefile
\ No newline at end of file
diff --git a/src/libsystemd/sd-bus/PORTING-DBUS1 b/src/libsystemd/sd-bus/PORTING-DBUS1
new file mode 100644
index 0000000..67af277
--- /dev/null
+++ b/src/libsystemd/sd-bus/PORTING-DBUS1
@@ -0,0 +1,550 @@
+A few hints on supporting kdbus as backend in your favorite D-Bus library.
+
+~~~
+
+Before you read this, have a look at the DIFFERENCES and
+GVARIANT_SERIALIZATION texts you find in the same directory where you
+found this.
+
+We invite you to port your favorite D-Bus protocol implementation
+over to kdbus. However, there are a couple of complexities
+involved. On kdbus we only speak GVariant marshaling, kdbus clients
+ignore traffic in dbus1 marshaling. Thus, you need to add a second,
+GVariant compatible marshaler to your library first.
+
+After you have done that: here's the basic principle how kdbus works:
+
+You connect to a bus by opening its bus node in /dev/kdbus/. All
+buses have a device node there, it starts with a numeric UID of the
+owner of the bus, followed by a dash and a string identifying the
+bus. The system bus is thus called /dev/kdbus/0-system, and for user
+buses the device node is /dev/kdbus/1000-user (if 1000 is your user
+id).
+
+(Before we proceed, please always keep a copy of libsystemd next
+to you, ultimately that's where the details are, this document simply
+is a rough overview to help you grok things.)
+
+CONNECTING
+
+To connect to a bus, simply open() its device node and issue the
+KDBUS_CMD_HELLO call. That's it. Now you are connected. Do not send
+Hello messages or so (as you would on dbus1), that does not exist for
+kdbus.
+
+The structure you pass to the ioctl will contain a couple of
+parameters that you need to know, to operate on the bus.
+
+There are two flags fields, one indicating features of the kdbus
+kernel side ("conn_flags"), the other one ("bus_flags") indicating
+features of the bus owner (i.e. systemd). Both flags fields are 64bit
+in width.
+
+When calling into the ioctl, you need to place your own supported
+feature bits into these fields. This tells the kernel about the
+features you support. When the ioctl returns, it will contain the
+features the kernel supports.
+
+If any of the higher 32bit are set on the two flags fields and your
+client does not know what they mean, it must disconnect. The upper
+32bit are used to indicate "incompatible" feature additions on the bus
+system, the lower 32bit indicate "compatible" feature additions. A
+client that does not support a "compatible" feature addition can go on
+communicating with the bus, however a client that does not support an
+"incompatible" feature must not proceed with the connection.
+
+The hello structure also contains another flags field "attach_flags"
+which indicates metadata that is optionally attached to all incoming
+messages. You probably want to set KDBUS_ATTACH_NAMES unconditionally
+in it. This has the effect that all well-known names of a sender are
+attached to all incoming messages. You need this information to
+implement matches that match on a message sender name correctly. Of
+course, you should only request the attachment of as little metadata
+fields as you need.
+
+The kernel will return in the "id" field your unique id. This is a
+simple numeric value. For compatibility with classic dbus1 simply
+format this as string and prefix ":0.".
+
+The kernel will also return the bloom filter size used for the signal
+broadcast bloom filter (see below).
+
+The kernel will also return the bus ID of the bus in a 128bit field.
+
+The pool size field specifies the size of the memory mapped buffer.
+After the calling the hello ioctl, you should memory map the kdbus
+fd. In this memory mapped region, the kernel will place all your incoming
+messages.
+
+SENDING MESSAGES
+
+Use the MSG_SEND ioctl to send a message to another peer. The ioctl
+takes a structure that contains a variety of fields:
+
+The flags field corresponds closely to the old dbus1 message header
+flags field, though the DONT_EXPECT_REPLY field got inverted into
+EXPECT_REPLY.
+
+The dst_id/src_id field contains the unique id of the destination and
+the sender. The sender field is overridden by the kernel usually, hence
+you shouldn't fill it in. The destination field can also take the
+special value KDBUS_DST_ID_BROADCAST for broadcast messages. For
+messages intended to a well-known name set the field to
+KDBUS_DST_ID_NAME, and attach the name in a special "items" entry to
+the message (see below).
+
+The payload field indicates the payload. For all dbus traffic it
+should carry the value 0x4442757344427573ULL. (Which encodes
+'DBusDBus').
+
+The cookie field corresponds with the "serial" field of classic
+dbus1. We simply renamed it here (and extended it to 64bit) since we
+didn't want to imply the monotonicity of the assignment the way the
+word "serial" indicates it.
+
+When sending a message that expects a reply, you need to set the
+EXPECT_REPLY flag in the message flag field. In this case you should
+also fill out the "timeout_ns" value which indicates the timeout in
+nsec for this call. If the peer does not respond in this time you will
+get a notification of a timeout. Note that this is also used for
+security purposes: a single reply messages is only allowed through the
+bus as long as the timeout has not ended. With this timeout value you
+hence "open a time window" in which the peer might respond to your
+request and the policy allows the response to go through.
+
+When sending a message that is a reply, you need to fill in the
+cookie_reply field, which is similar to the reply_serial field of
+dbus1. Note that a message cannot have EXPECT_REPLY and a reply_serial
+at the same time!
+
+This pretty much explains the ioctl header. The actual payload of the
+data is now referenced in additional items that are attached to this
+ioctl header structure at the end. When sending a message, you attach
+items of the type PAYLOAD_VEC, PAYLOAD_MEMFD, FDS, BLOOM, DST_NAME to
+it:
+
+ KDBUS_ITEM_PAYLOAD_VEC: contains a pointer + length pair for
+ referencing arbitrary user memory. This is how you reference most
+ of your data. It's a lot like the good old iovec structure of glibc.
+
+ KDBUS_ITEM_PAYLOAD_MEMFD: for large data blocks it is preferable
+ to send prepared "memfds" (see below) over. This item contains an
+ fd for a memfd plus a size.
+
+ KDBUS_ITEM_PAYLOAD_FDS: for sending over fds attach an item of this
+ type with an array of fds.
+
+ KDBUS_ITEM_BLOOM: the calculated bloom filter of this message, only
+ for undirected (broadcast) message.
+
+ KDBUS_DST_NAME: for messages that are directed to a well-known name
+ (instead of a unique name), this item contains the well-known name
+ field.
+
+A single message may consists of no, one or more payload items of type
+PAYLOAD_VEC or PAYLOAD_MEMFD. D-Bus protocol implementations should
+treat them as a single block that just happens to be split up into
+multiple items. Some restrictions apply however:
+
+ The message header in its entirety must be contained in a single
+ PAYLOAD_VEC item.
+
+ You may only split your message up right in front of each GVariant
+ contained in the payload, as well is immediately before framing of a
+ Gvariant, as well after as any padding bytes if there are any. The
+ padding bytes must be wholly contained in the preceding
+ PAYLOAD_VEC/PAYLOAD_MEMFD item. You may not split up simple types
+ nor arrays of trivial types. The latter is necessary to allow APIs
+ to return direct pointers to linear chunks of fixed size trivial
+ arrays. Examples: The simple types "u", "s", "t" have to be in the
+ same payload item. The array of simple types "ay", "ai" have to be
+ fully in contained in the same payload item. For an array "as" or
+ "a(si)" the only restriction however is to keep each string
+ individually in an uninterrupted item, to keep the framing of each
+ element and the array in a single uninterrupted item, however the
+ various strings might end up in different items.
+
+Note again, that splitting up messages into separate items is up to the
+implementation. Also note that the kdbus kernel side might merge
+separate items if it deems this to be useful. However, the order in
+which items are contained in the message is left untouched.
+
+PAYLOAD_MEMFD items allow zero-copy data transfer (see below regarding
+the memfd concept). Note however that the overhead of mapping these
+makes them relatively expensive, and only worth the trouble for memory
+blocks > 128K (this value appears to be quite universal across
+architectures, as we tested). Thus we recommend sending PAYLOAD_VEC
+items over for small messages and restore to PAYLOAD_MEMFD items for
+messages > 128K. Since while building up the message you might not
+know yet whether it will grow beyond this boundary a good approach is
+to simply build the message unconditionally in a memfd
+object. However, when the message is sealed to be sent away check for
+the size limit. If the size of the message is < 128K, then simply send
+the data as PAYLOAD_VEC and reuse the memfd. If it is >= 128K, seal
+the memfd and send it as PAYLOAD_MEMFD, and allocate a new memfd for
+the next message.
+
+RECEIVING MESSAGES
+
+Use the MSG_RECV ioctl to read a message from kdbus. This will return
+an offset into the pool memory map, relative to its beginning.
+
+The received message structure more or less follows the structure of
+the message originally sent. However, certain changes have been
+made. In the header the src_id field will be filled in.
+
+The payload items might have gotten merged and PAYLOAD_VEC items are
+not used. Instead, you will only find PAYLOAD_OFF and PAYLOAD_MEMFD
+items. The former contain an offset and size into your memory mapped
+pool where you find the payload.
+
+If during the HELLO ioctl you asked for getting metadata attached to
+your message, you will find additional KDBUS_ITEM_CREDS,
+KDBUS_ITEM_PID_COMM, KDBUS_ITEM_TID_COMM, KDBUS_ITEM_TIMESTAMP,
+KDBUS_ITEM_EXE, KDBUS_ITEM_CMDLINE, KDBUS_ITEM_CGROUP,
+KDBUS_ITEM_CAPS, KDBUS_ITEM_SECLABEL, KDBUS_ITEM_AUDIT items that
+contain this metadata. This metadata will be gathered from the sender
+at the point in time it sends the message. This information is
+uncached, and since it is appended by the kernel, trustable. The
+KDBUS_ITEM_SECLABEL item usually contains the SELinux security label,
+if it is used.
+
+After processing the message you need to call the KDBUS_CMD_FREE
+ioctl, which releases the message from the pool, and allows the kernel
+to store another message there. Note that the memory used by the pool
+is ordinary anonymous, swappable memory that is backed by tmpfs. Hence
+there is no need to copy the message out of it quickly, instead you
+can just leave it there as long as you need it and release it via the
+FREE ioctl only after that's done.
+
+BLOOM FILTERS
+
+The kernel does not understand dbus marshaling, it will not look into
+the message payload. To allow clients to subscribe to specific subsets
+of the broadcast matches we employ bloom filters.
+
+When broadcasting messages, a bloom filter needs to be attached to the
+message in a KDBUS_ITEM_BLOOM item (and only for broadcasting
+messages!). If you don't know what bloom filters are, read up now on
+Wikipedia. In short: they are a very efficient way how to
+probabilistically check whether a certain word is contained in a
+vocabulary. It knows no false negatives, but it does know false
+positives.
+
+The bloom filter that needs to be included has the parameters m=512
+(bits in the filter), k=8 (nr of hash functions). The underlying hash
+function is SipHash-2-4. We calculate two hash values for an input
+strings, one with the hash key b9660bf0467047c18875c49c54b9bd15 (this
+is supposed to be read as a series of 16 hexadecimal formatted
+bytes), and one with the hash key
+aaa154a2e0714b39bfe1dd2e9fc54a3b. This results in two 64bit hash
+values, A and B. The 8 hash functions for the bloom filter require a 9
+bit output each (since m=512=2^9), to generate these we XOR combine
+the first 8 bit of A shifted to the left by 1, with the first 8 bit of
+B. Then, for the next hash function we use the second 8 bit pair, and
+so on.
+
+For each message to send across the bus we populate the bloom filter
+with all possible matchable strings. If a client then wants to
+subscribe to messages of this type, it simply tells the kernel to test
+its own calculated bit mask against the bloom filter of each message.
+
+More specifically, the following strings are added to the bloom filter
+of each message that is broadcasted:
+
+ The string "interface:" suffixed by the interface name
+
+ The string "member:" suffixed by the member name
+
+ The string "path:" suffixed by the path name
+
+ The string "path-slash-prefix:" suffixed with the path name, and
+ also all prefixes of the path name (cut off at "/"), also prefixed
+ with "path-slash-prefix".
+
+ The string "message-type:" suffixed with the strings "signal",
+ "method_call", "error" or "method_return" for the respective message
+ type of the message.
+
+ If the first argument of the message is a string, "arg0:" suffixed
+ with the first argument.
+
+ If the first argument of the message is a string, "arg0-dot-prefix"
+ suffixed with the first argument, and also all prefixes of the
+ argument (cut off at "."), also prefixed with "arg0-dot-prefix".
+
+ If the first argument of the message is a string,
+ "arg0-slash-prefix" suffixed with the first argument, and also all
+ prefixes of the argument (cut off at "/"), also prefixed with
+ "arg0-slash-prefix".
+
+ Similar for all further arguments that are strings up to 63, for the
+ arguments and their "dot" and "slash" prefixes. On the first
+ argument that is not a string, addition to the bloom filter should be
+ stopped however.
+
+(Note that the bloom filter does not contain sender nor receiver
+names!)
+
+When a client wants to subscribe to messages matching a certain
+expression, it should calculate the bloom mask following the same
+algorithm. The kernel will then simply test the mask against the
+attached bloom filters.
+
+Note that bloom filters are probabilistic, which means that clients
+might get messages they did not expect. Your bus protocol
+implementation must be capable of dealing with these unexpected
+messages (which it needs to anyway, given that transfers are
+relatively unrestricted on kdbus and people can send you all kinds of
+non-sense).
+
+INSTALLING MATCHES
+
+To install matches for broadcast messages, use the KDBUS_CMD_ADD_MATCH
+ioctl. It takes a structure that contains an encoded match expression,
+and that is followed by one or more items, which are combined in an
+AND way. (Meaning: a message is matched exactly when all items
+attached to the original ioctl struct match).
+
+To match against other user messages add a KDBUS_ITEM_BLOOM item in
+the match (see above). Note that the bloom filter does not include
+matches to the sender names. To additionally check against sender
+names, use the KDBUS_ITEM_ID (for unique id matches) and
+KDBUS_ITEM_NAME (for well-known name matches) item types.
+
+To match against kernel generated messages (see below) you should add
+items of the same type as the kernel messages include,
+i.e. KDBUS_ITEM_NAME_ADD, KDBUS_ITEM_NAME_REMOVE,
+KDBUS_ITEM_NAME_CHANGE, KDBUS_ITEM_ID_ADD, KDBUS_ITEM_ID_REMOVE and
+fill them out. Note however, that you have some wildcards in this
+case, for example the .id field of KDBUS_ITEM_ADD/KDBUS_ITEM_REMOVE
+structures may be set to 0 to match against any id addition/removal.
+
+Note that dbus match strings do no map 1:1 to these ioctl() calls. In
+many cases (where the match string is "underspecified") you might need
+to issue up to six different ioctl() calls for the same match. For
+example, the empty match (which matches against all messages), would
+translate into one KDBUS_ITEM_BLOOM ioctl, one KDBUS_ITEM_NAME_ADD,
+one KDBUS_ITEM_NAME_CHANGE, one KDBUS_ITEM_NAME_REMOVE, one
+KDBUS_ITEM_ID_ADD and one KDBUS_ITEM_ID_REMOVE.
+
+When creating a match, you may attach a "cookie" value to them, which
+is used for deleting this match again. The cookie can be selected freely
+by the client. When issuing KDBUS_CMD_REMOVE_MATCH, simply pass the
+same cookie as before and all matches matching the same "cookie" value
+will be removed. This is particularly handy for the case where multiple
+ioctl()s are added for a single match strings.
+
+MEMFDS
+
+The "memfd" concept is used for zero-copy data transfers (see
+above). memfds are file descriptors to memory chunks of arbitrary
+sizes. If you have a memfd you can mmap() it to get access to the data
+it contains or write to it. They are comparable to file descriptors to
+unlinked files on a tmpfs, or to anonymous memory that one may refer
+to with an fd. They have one particular property: they can be
+"sealed". A memfd that is "sealed" is protected from alteration. Only
+memfds that are currently not mapped and to which a single fd refers
+may be sealed (they may also be unsealed in that case).
+
+The concept of "sealing" makes memfds useful for using them as
+transport for kdbus messages: only when the receiver knows that the
+message it has received cannot change while looking at, it can safely
+parse it without having to copy it to a safe memory area. memfds can also
+be reused in multiple messages. A sender may send the same memfd to
+multiple peers, and since it is sealed, it can be sure that the receiver
+will not be able to modify it. "Sealing" hence provides both sides of
+a transaction with the guarantee that the data stays constant and is
+reusable.
+
+memfds are a generic concept that can be used outside of the immediate
+kdbus usecase. You can send them across AF_UNIX sockets too, sealed or
+unsealed. In kdbus themselves, they can be used to send zero-copy
+payloads, but may also be sent as normal fds.
+
+memfds are allocated with the KDBUS_CMD_MEMFD_NEW ioctl. After allocation,
+simply memory map them and write to them. To set their size, use
+KDBUS_CMD_MEMFD_SIZE_SET. Note that memfds will be increased in size
+automatically if you touch previously unallocated pages. However, the
+size will only be increased in multiples of the page size in that
+case. Thus, in almost all cases, an explicit KDBUS_CMD_MEMFD_SIZE_SET
+is necessary, since it allows setting memfd sizes in finer
+granularity. To seal a memfd use the KDBUS_CMD_MEMFD_SEAL_SET ioctl
+call. It will only succeed if the caller has the only fd reference to
+the memfd open, and if the memfd is currently unmapped.
+
+If memfds are shared, keep in mind that the file pointer used by
+write/read/seek is shared too, only pread/pwrite are safe to use
+in that case.
+
+memfds may be sent across kdbus via KDBUS_ITEM_PAYLOAD_MEMFD items
+attached to messages. If this is done, the data included in the memfd
+is considered part of the payload stream of a message, and are treated
+the same way as KDBUS_ITEM_PAYLOAD_VEC by the receiving side. It is
+possible to interleave KDBUS_ITEM_PAYLOAD_MEMFD and
+KDBUS_ITEM_PAYLOAD_VEC items freely, by the reader they will be
+considered a single stream of bytes in the order these items appear in
+the message, that just happens to be split up at various places
+(regarding rules how they may be split up, see above). The kernel will
+refuse taking KDBUS_ITEM_PAYLOAD_MEMFD items that refer to memfds that
+are not sealed.
+
+Note that sealed memfds may be unsealed again if they are not mapped
+you have the only fd reference to them.
+
+Alternatively to sending memfds as KDBUS_ITEM_PAYLOAD_MEMFD items
+(where they are just a part of the payload stream of a message) you can
+also simply attach any memfd to a message using
+KDBUS_ITEM_PAYLOAD_FDS. In this case, the memfd contents is not
+considered part of the payload stream of the message, but simply fds
+like any other, that happen to be attached to the message.
+
+MESSAGES FROM THE KERNEL
+
+A couple of messages previously generated by the dbus1 bus driver are
+now generated by the kernel. Since the kernel does not understand the
+payload marshaling, they are generated by the kernel in a different
+format. This is indicated with the "payload type" field of the
+messages set to 0. Library implementations should take these messages
+and synthesize traditional driver messages for them on reception.
+
+More specifically:
+
+ Instead of the NameOwnerChanged, NameLost, NameAcquired signals
+ there are kernel messages containing KDBUS_ITEM_NAME_ADD,
+ KDBUS_ITEM_NAME_REMOVE, KDBUS_ITEM_NAME_CHANGE, KDBUS_ITEM_ID_ADD,
+ KDBUS_ITEM_ID_REMOVE items are generated (each message will contain
+ exactly one of these items). Note that in libsystemd we have
+ obsoleted NameLost/NameAcquired messages, since they are entirely
+ redundant to NameOwnerChanged. This library will hence only
+ synthesize NameOwnerChanged messages from these kernel messages,
+ and never generate NameLost/NameAcquired. If your library needs to
+ stay compatible to the old dbus1 userspace, you possibly might need
+ to synthesize both a NameOwnerChanged and NameLost/NameAcquired
+ message from the same kernel message.
+
+ When a method call times out, a KDBUS_ITEM_REPLY_TIMEOUT message is
+ generated. This should be synthesized into a method error reply
+ message to the original call.
+
+ When a method call fails because the peer terminated the connection
+ before responding, a KDBUS_ITEM_REPLY_DEAD message is
+ generated. Similarly, it should be synthesized into a method error
+ reply message.
+
+For synthesized messages we recommend setting the cookie field to
+(uint32_t) -1 (and not (uint64_t) -1!), so that the cookie is not 0
+(which the dbus1 spec does not allow), but clearly recognizable as
+synthetic.
+
+Note that the KDBUS_ITEM_NAME_XYZ messages will actually inform you
+about all kinds of names, including activatable ones. Classic dbus1
+NameOwnerChanged messages OTOH are only generated when a name is
+really acquired on the bus and not just simply activatable. This means
+you must explicitly check for the case where an activatable name
+becomes acquired or an acquired name is lost and returns to be
+activatable.
+
+NAME REGISTRY
+
+To acquire names on the bus, use the KDBUS_CMD_NAME_ACQUIRE ioctl(). It
+takes a flags field similar to dbus1's RequestName() bus driver call,
+however the NO_QUEUE flag got inverted into a QUEUE flag instead.
+
+To release a previously acquired name use the KDBUS_CMD_NAME_RELEASE
+ioctl().
+
+To list acquired names use the KDBUS_CMD_CONN_INFO ioctl. It may be
+used to list unique names, well known names as well as activatable
+names and clients currently queuing for ownership of a well-known
+name. The ioctl will return an offset into the memory pool. After
+reading all the data you need, you need to release this via the
+KDBUS_CMD_FREE ioctl(), similar how you release a received message.
+
+CREDENTIALS
+
+kdbus can optionally attach various kinds of metadata about the sender at
+the point of time of sending ("credentials") to messages, on request
+of the receiver. This is both supported on directed and undirected
+(broadcast) messages. The metadata to attach is selected at time of
+the HELLO ioctl of the receiver via a flags field (see above). Note
+that clients must be able to handle that messages contain more
+metadata than they asked for themselves, to simplify implementation of
+broadcasting in the kernel. The receiver should not rely on this data
+to be around though, even though it will be correct if it happens to
+be attached. In order to avoid programming errors in applications, we
+recommend though not passing this data on to clients that did not
+explicitly ask for it.
+
+Credentials may also be queried for a well-known or unique name. Use
+the KDBUS_CMD_CONN_INFO for this. It will return an offset to the pool
+area again, which will contain the same credential items as messages
+have attached. Note that when issuing the ioctl, you can select a
+different set of credentials to gather, than what was originally requested
+for being attached to incoming messages.
+
+Credentials are always specific to the sender namespace that was
+current at the time of sending, and of the process that opened the
+bus connection at the time of opening it. Note that this latter data
+is cached!
+
+POLICY
+
+The kernel enforces only very limited policy on names. It will not do
+access filtering by userspace payload, and thus not by interface or
+method name.
+
+This ultimately means that most fine-grained policy enforcement needs
+to be done by the receiving process. We recommend using PolicyKit for
+any more complex checks. However, libraries should make simple static
+policy decisions regarding privileged/unprivileged method calls
+easy. We recommend doing this by enabling KDBUS_ATTACH_CAPS and
+KDBUS_ATTACH_CREDS for incoming messages, and then discerning client
+access by some capability, or if sender and receiver UIDs match.
+
+BUS ADDRESSES
+
+When connecting to kdbus use the "kernel:" protocol prefix in DBus
+address strings. The device node path is encoded in its "path="
+parameter.
+
+Client libraries should use the following connection string when
+connecting to the system bus:
+
+ kernel:path=/dev/kdbus/0-system/bus;unix:path=/run/dbus/system_bus_socket
+
+This will ensure that kdbus is preferred over the legacy AF_UNIX
+socket, but compatibility is kept. For the user bus use:
+
+ kernel:path=/dev/kdbus/$UID-user/bus;unix:path=$XDG_RUNTIME_DIR/bus
+
+With $UID replaced by the callers numer user ID, and $XDG_RUNTIME_DIR
+following the XDG basedir spec.
+
+Of course the $DBUS_SYSTEM_BUS_ADDRESS and $DBUS_SESSION_BUS_ADDRESS
+variables should still take precedence.
+
+DBUS SERVICE FILES
+
+Activatable services for kdbus may not use classic dbus1 service
+activation files. Instead, programs should drop in native systemd
+.service and .busname unit files, so that they are treated uniformly
+with other types of units and activation of the system.
+
+Note that this results in a major difference to classic dbus1:
+activatable bus names can be established at any time in the boot process.
+This is unlike dbus1 where activatable names are unconditionally available
+as long as dbus-daemon is running. Being able to control when
+activatable names are established is essential to allow usage of kdbus
+during early boot and in initrds, without the risk of triggering
+services too early.
+
+DISCLAIMER
+
+This all is so far just the status quo. We are putting this together, because
+we are quite confident that further API changes will be smaller, but
+to make this very clear: this is all subject to change, still!
+
+We invite you to port over your favorite dbus library to this new
+scheme, but please be prepared to make minor changes when we still
+change these interfaces!
diff --git a/src/libsystemd/sd-bus/bus-bloom.c b/src/libsystemd/sd-bus/bus-bloom.c
new file mode 100644
index 0000000..9e51334
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-bloom.c
@@ -0,0 +1,99 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "siphash24.h"
+#include "bus-bloom.h"
+
+static inline void set_bit(uint64_t filter[], unsigned b) {
+ filter[b >> 6] |= 1ULL << (b & 63);
+}
+
+#define HASH_KEY1 SD_ID128_MAKE(b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15)
+#define HASH_KEY2 SD_ID128_MAKE(aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b)
+
+static void bloom_add_data(uint64_t filter[BLOOM_SIZE/8], const void *data, size_t n) {
+ uint8_t a[8], b[8];
+ unsigned k = 0;
+
+ /*
+ * Our bloom filter has the following parameters:
+ *
+ * m=512 (bits in the filter)
+ * k=8 (hash functions)
+ *
+ * We calculate a two 64bit SipHash values (for two fixed but
+ * randomly generated hash keys) of which we use 8 parts of 9 bits
+ * as individual hash functions.
+ *
+ */
+
+ siphash24(a, data, n, HASH_KEY1.bytes);
+ siphash24(b, data, n, HASH_KEY2.bytes);
+
+ assert_cc(BLOOM_SIZE*8 == 512);
+
+ for (k = 0; k < 8; k++)
+ set_bit(filter, ((uint16_t) a[k] << 1) ^ (uint16_t) b[k]);
+
+ /* log_debug("bloom: adding <%.*s>", (int) n, (char*) data); */
+}
+
+void bloom_add_pair(uint64_t filter[BLOOM_SIZE/8], const char *a, const char *b) {
+ size_t n;
+ char *c;
+
+ assert(filter);
+ assert(a);
+ assert(b);
+
+ n = strlen(a) + 1 + strlen(b);
+ c = alloca(n + 1);
+ strcpy(stpcpy(stpcpy(c, a), ":"), b);
+
+ bloom_add_data(filter, c, n);
+}
+
+void bloom_add_prefixes(uint64_t filter[BLOOM_SIZE/8], const char *a, const char *b, char sep) {
+ size_t n;
+ char *c, *p;
+
+ assert(filter);
+ assert(a);
+ assert(b);
+
+ n = strlen(a) + 1 + strlen(b);
+ c = alloca(n + 1);
+
+ p = stpcpy(stpcpy(c, a), ":");
+ strcpy(p, b);
+
+ for (;;) {
+ char *e;
+
+ e = strrchr(p, sep);
+ if (!e || e == p)
+ break;
+
+ *e = 0;
+ bloom_add_data(filter, c, e - c);
+ }
+}
diff --git a/src/libsystemd/sd-bus/bus-bloom.h b/src/libsystemd/sd-bus/bus-bloom.h
new file mode 100644
index 0000000..422eb4e
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-bloom.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#define BLOOM_SIZE 64
+
+void bloom_add_pair(uint64_t filter[BLOOM_SIZE/8], const char *a, const char *b);
+void bloom_add_prefixes(uint64_t filter[BLOOM_SIZE/8], const char *a, const char *b, char sep);
diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c
new file mode 100644
index 0000000..d330363
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-container.c
@@ -0,0 +1,241 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "util.h"
+#include "fileio.h"
+#include "bus-internal.h"
+#include "bus-socket.h"
+#include "bus-container.h"
+
+int bus_container_connect_socket(sd_bus *b) {
+ _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
+ pid_t leader, child;
+ siginfo_t si;
+ int r;
+
+ assert(b);
+ assert(b->input_fd < 0);
+ assert(b->output_fd < 0);
+
+ r = container_get_leader(b->machine, &leader);
+ if (r < 0)
+ return r;
+
+ r = namespace_open(leader, &pidnsfd, &mntnsfd, &rootfd);
+ if (r < 0)
+ return r;
+
+ b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (b->input_fd < 0)
+ return -errno;
+
+ b->output_fd = b->input_fd;
+
+ bus_socket_setup(b);
+
+ child = fork();
+ if (child < 0)
+ return -errno;
+
+ if (child == 0) {
+ pid_t grandchild;
+
+ r = namespace_enter(pidnsfd, mntnsfd, rootfd);
+ if (r < 0)
+ _exit(255);
+
+ /* We just changed PID namespace, however it will only
+ * take effect on the children we now fork. Hence,
+ * let's fork another time, and connect from this
+ * grandchild, so that SO_PEERCRED of our connection
+ * comes from a process from within the container, and
+ * not outside of it */
+
+ grandchild = fork();
+ if (grandchild < 0)
+ _exit(255);
+
+ if (grandchild == 0) {
+
+ r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
+ if (r < 0) {
+ if (errno == EINPROGRESS)
+ _exit(1);
+
+ _exit(255);
+ }
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ r = wait_for_terminate(grandchild, &si);
+ if (r < 0)
+ _exit(255);
+
+ if (si.si_code != CLD_EXITED)
+ _exit(255);
+
+ _exit(si.si_status);
+ }
+
+ r = wait_for_terminate(child, &si);
+ if (r < 0)
+ return r;
+
+ if (si.si_code != CLD_EXITED)
+ return -EIO;
+
+ if (si.si_status == 1)
+ return 1;
+
+ if (si.si_status != EXIT_SUCCESS)
+ return -EIO;
+
+ return bus_socket_start_auth(b);
+}
+
+int bus_container_connect_kernel(sd_bus *b) {
+ _cleanup_close_pipe_ int pair[2] = { -1, -1 };
+ _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+ pid_t leader, child;
+ siginfo_t si;
+ int r;
+ _cleanup_close_ int fd = -1;
+
+ assert(b);
+ assert(b->input_fd < 0);
+ assert(b->output_fd < 0);
+
+ r = container_get_leader(b->machine, &leader);
+ if (r < 0)
+ return r;
+
+ r = namespace_open(leader, &pidnsfd, &mntnsfd, &rootfd);
+ if (r < 0)
+ return r;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+ return -errno;
+
+ child = fork();
+ if (child < 0)
+ return -errno;
+
+ if (child == 0) {
+ pid_t grandchild;
+
+ close_nointr_nofail(pair[0]);
+ pair[0] = -1;
+
+ r = namespace_enter(pidnsfd, mntnsfd, rootfd);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ /* We just changed PID namespace, however it will only
+ * take effect on the children we now fork. Hence,
+ * let's fork another time, and connect from this
+ * grandchild, so that kdbus only sees the credentials
+ * of this process which comes from within the
+ * container, and not outside of it */
+
+ grandchild = fork();
+ if (grandchild < 0)
+ _exit(EXIT_FAILURE);
+
+ if (grandchild == 0) {
+
+ fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ _exit(EXIT_FAILURE);
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+
+ mh.msg_controllen = cmsg->cmsg_len;
+
+ if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ r = wait_for_terminate(grandchild, &si);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ if (si.si_code != CLD_EXITED)
+ _exit(EXIT_FAILURE);
+
+ _exit(si.si_status);
+ }
+
+ close_nointr_nofail(pair[1]);
+ pair[1] = -1;
+
+ r = wait_for_terminate(child, &si);
+ if (r < 0)
+ return r;
+
+ if (si.si_code != CLD_EXITED)
+ return -EIO;
+
+ if (si.si_status != EXIT_SUCCESS)
+ return -EIO;
+
+ if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
+ return -errno;
+
+ for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ int *fds;
+ unsigned n_fds;
+
+ fds = (int*) CMSG_DATA(cmsg);
+ n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+
+ if (n_fds != 1) {
+ close_many(fds, n_fds);
+ return -EIO;
+ }
+
+ fd = fds[0];
+ }
+
+ b->input_fd = b->output_fd = fd;
+ fd = -1;
+
+ return bus_kernel_take_fd(b);
+}
diff --git a/src/libsystemd/sd-bus/bus-container.h b/src/libsystemd/sd-bus/bus-container.h
new file mode 100644
index 0000000..c6f757a
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-container.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+int bus_container_connect_socket(sd_bus *b);
+int bus_container_connect_kernel(sd_bus *b);
diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c
new file mode 100644
index 0000000..e7e9ba0
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-control.c
@@ -0,0 +1,1217 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <stddef.h>
+#include <errno.h>
+
+#include "strv.h"
+#include "sd-bus.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-control.h"
+#include "bus-bloom.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+
+_public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(unique, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = bus_ensure_running(bus);
+ if (r < 0)
+ return r;
+
+ *unique = bus->unique_name;
+ return 0;
+}
+
+static int bus_request_name_kernel(sd_bus *bus, const char *name, uint64_t flags) {
+ struct kdbus_cmd_name *n;
+ size_t size, l;
+ int r;
+
+ assert(bus);
+ assert(name);
+
+ l = strlen(name);
+ size = offsetof(struct kdbus_cmd_name, name) + l + 1;
+ n = alloca0(size);
+ n->size = size;
+ kdbus_translate_request_name_flags(flags, (uint64_t *) &n->flags);
+ memcpy(n->name, name, l+1);
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(n, n->size);
+#endif
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_NAME_ACQUIRE, n);
+ if (r < 0)
+ return -errno;
+
+ if (n->flags & KDBUS_NAME_IN_QUEUE)
+ return 0;
+
+ return 1;
+}
+
+static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ uint32_t ret, param = 0;
+ int r;
+
+ assert(bus);
+ assert(name);
+
+ if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT)
+ param |= BUS_NAME_ALLOW_REPLACEMENT;
+ if (flags & SD_BUS_NAME_REPLACE_EXISTING)
+ param |= BUS_NAME_REPLACE_EXISTING;
+ if (!(flags & SD_BUS_NAME_QUEUE))
+ param |= BUS_NAME_DO_NOT_QUEUE;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "RequestName",
+ NULL,
+ &reply,
+ "su",
+ name,
+ param);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "u", &ret);
+ if (r < 0)
+ return r;
+
+ if (ret == BUS_NAME_ALREADY_OWNER)
+ return -EALREADY;
+ else if (ret == BUS_NAME_EXISTS)
+ return -EEXIST;
+ else if (ret == BUS_NAME_IN_QUEUE)
+ return 0;
+ else if (ret == BUS_NAME_PRIMARY_OWNER)
+ return 1;
+
+ return -EIO;
+}
+
+_public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) {
+ assert_return(bus, -EINVAL);
+ assert_return(name, -EINVAL);
+ assert_return(bus->bus_client, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL);
+ assert_return(service_name_is_valid(name), -EINVAL);
+ assert_return(name[0] != ':', -EINVAL);
+
+ if (bus->is_kernel)
+ return bus_request_name_kernel(bus, name, flags);
+ else
+ return bus_request_name_dbus1(bus, name, flags);
+}
+
+static int bus_release_name_kernel(sd_bus *bus, const char *name) {
+ struct kdbus_cmd_name *n;
+ size_t l;
+ int r;
+
+ assert(bus);
+ assert(name);
+
+ l = strlen(name);
+ n = alloca0(offsetof(struct kdbus_cmd_name, name) + l + 1);
+ n->size = offsetof(struct kdbus_cmd_name, name) + l + 1;
+ memcpy(n->name, name, l+1);
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(n, n->size);
+#endif
+ r = ioctl(bus->input_fd, KDBUS_CMD_NAME_RELEASE, n);
+ if (r < 0)
+ return -errno;
+
+ return n->flags;
+}
+
+static int bus_release_name_dbus1(sd_bus *bus, const char *name) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ uint32_t ret;
+ int r;
+
+ assert(bus);
+ assert(name);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "ReleaseName",
+ NULL,
+ &reply,
+ "s",
+ name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "u", &ret);
+ if (r < 0)
+ return r;
+ if (ret == BUS_NAME_NON_EXISTENT)
+ return -ESRCH;
+ if (ret == BUS_NAME_NOT_OWNER)
+ return -EADDRINUSE;
+ if (ret == BUS_NAME_RELEASED)
+ return 0;
+
+ return -EINVAL;
+}
+
+_public_ int sd_bus_release_name(sd_bus *bus, const char *name) {
+ assert_return(bus, -EINVAL);
+ assert_return(name, -EINVAL);
+ assert_return(bus->bus_client, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(service_name_is_valid(name), -EINVAL);
+ assert_return(name[0] != ':', -EINVAL);
+
+ if (bus->is_kernel)
+ return bus_release_name_kernel(bus, name);
+ else
+ return bus_release_name_dbus1(bus, name);
+}
+
+static int kernel_get_list(sd_bus *bus, uint64_t flags, char ***x) {
+ struct kdbus_cmd_name_list cmd = {};
+ struct kdbus_name_list *name_list;
+ struct kdbus_cmd_name *name;
+ uint64_t previous_id = 0;
+ int r;
+
+ /* Caller will free half-constructed list on failure... */
+
+ cmd.flags = flags;
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_NAME_LIST, &cmd);
+ if (r < 0)
+ return -errno;
+
+ name_list = (struct kdbus_name_list *) ((uint8_t *) bus->kdbus_buffer + cmd.offset);
+
+ KDBUS_ITEM_FOREACH(name, name_list, names) {
+
+ if ((flags & KDBUS_NAME_LIST_UNIQUE) && name->owner_id != previous_id) {
+ char *n;
+
+ if (asprintf(&n, ":1.%llu", (unsigned long long) name->owner_id) < 0)
+ return -ENOMEM;
+
+ r = strv_push(x, n);
+ if (r < 0) {
+ free(n);
+ return -ENOMEM;
+ }
+
+ previous_id = name->owner_id;
+ }
+
+ if (name->size > sizeof(*name) && service_name_is_valid(name->name)) {
+ r = strv_extend(x, name->name);
+ if (r < 0)
+ return -ENOMEM;
+ }
+ }
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_FREE, &cmd.offset);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int bus_list_names_kernel(sd_bus *bus, char ***acquired, char ***activatable) {
+ _cleanup_strv_free_ char **x = NULL, **y = NULL;
+ int r;
+
+ if (acquired) {
+ r = kernel_get_list(bus, KDBUS_NAME_LIST_UNIQUE | KDBUS_NAME_LIST_NAMES, &x);
+ if (r < 0)
+ return r;
+ }
+
+ if (activatable) {
+ r = kernel_get_list(bus, KDBUS_NAME_LIST_ACTIVATORS, &y);
+ if (r < 0)
+ return r;
+
+ *activatable = y;
+ y = NULL;
+ }
+
+ if (acquired) {
+ *acquired = x;
+ x = NULL;
+ }
+
+ return 0;
+}
+
+static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatable) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_strv_free_ char **x = NULL, **y = NULL;
+ int r;
+
+ if (acquired) {
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "ListNames",
+ NULL,
+ &reply,
+ NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(reply, &x);
+ if (r < 0)
+ return r;
+
+ reply = sd_bus_message_unref(reply);
+ }
+
+ if (activatable) {
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "ListActivatableNames",
+ NULL,
+ &reply,
+ NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(reply, &y);
+ if (r < 0)
+ return r;
+
+ *activatable = y;
+ y = NULL;
+ }
+
+ if (acquired) {
+ *acquired = x;
+ x = NULL;
+ }
+
+ return 0;
+}
+
+_public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) {
+ assert_return(bus, -EINVAL);
+ assert_return(acquired || activatable, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (bus->is_kernel)
+ return bus_list_names_kernel(bus, acquired, activatable);
+ else
+ return bus_list_names_dbus1(bus, acquired, activatable);
+}
+
+static int bus_get_owner_kdbus(
+ sd_bus *bus,
+ const char *name,
+ uint64_t mask,
+ sd_bus_creds **creds) {
+
+ _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
+ struct kdbus_cmd_conn_info *cmd;
+ struct kdbus_conn_info *conn_info;
+ struct kdbus_item *item;
+ size_t size;
+ uint64_t m, id;
+ int r;
+
+ r = bus_kernel_parse_unique_name(name, &id);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ size = offsetof(struct kdbus_cmd_conn_info, name);
+ cmd = alloca0(size);
+ cmd->id = id;
+ } else {
+ size = offsetof(struct kdbus_cmd_conn_info, name) + strlen(name) + 1;
+ cmd = alloca0(size);
+ strcpy(cmd->name, name);
+ }
+ cmd->flags = KDBUS_ATTACH_NAMES;
+
+ cmd->size = size;
+ r = ioctl(bus->input_fd, KDBUS_CMD_CONN_INFO, cmd);
+ if (r < 0)
+ return -errno;
+
+ conn_info = (struct kdbus_conn_info *) ((uint8_t *) bus->kdbus_buffer + cmd->offset);
+
+ /* Non-activated names are considered not available */
+ if (conn_info->flags & KDBUS_HELLO_ACTIVATOR)
+ return name[0] == ':' ? -ENXIO : -ENOENT;
+
+ c = bus_creds_new();
+ if (!c)
+ return -ENOMEM;
+
+ if (mask & SD_BUS_CREDS_UNIQUE_NAME) {
+ if (asprintf(&c->unique_name, ":1.%llu", (unsigned long long) conn_info->id) < 0)
+ return -ENOMEM;
+
+ c->mask |= SD_BUS_CREDS_UNIQUE_NAME;
+ }
+
+ KDBUS_ITEM_FOREACH(item, conn_info, items) {
+
+ switch (item->type) {
+
+ case KDBUS_ITEM_CREDS:
+ m = (SD_BUS_CREDS_UID | SD_BUS_CREDS_GID | SD_BUS_CREDS_PID) & mask;
+
+ if (m) {
+ c->uid = (uid_t) item->creds.uid;
+ c->pid = (pid_t) item->creds.pid;
+ c->gid = (gid_t) item->creds.gid;
+ c->mask |= m;
+ }
+
+ if (mask & SD_BUS_CREDS_TID && item->creds.tid > 0) {
+ c->tid = (pid_t) item->creds.tid;
+ c->mask |= SD_BUS_CREDS_TID;
+ }
+
+ if (mask & SD_BUS_CREDS_PID_STARTTIME && item->creds.starttime > 0) {
+ c->pid_starttime = item->creds.starttime;
+ c->mask |= SD_BUS_CREDS_PID_STARTTIME;
+ }
+
+ break;
+
+ case KDBUS_ITEM_PID_COMM:
+ if (mask & SD_BUS_CREDS_COMM) {
+ c->comm = strdup(item->str);
+ if (!c->comm) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->mask |= SD_BUS_CREDS_COMM;
+ }
+ break;
+
+ case KDBUS_ITEM_TID_COMM:
+ if (mask & SD_BUS_CREDS_TID_COMM) {
+ c->tid_comm = strdup(item->str);
+ if (!c->tid_comm) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->mask |= SD_BUS_CREDS_TID_COMM;
+ }
+ break;
+
+ case KDBUS_ITEM_EXE:
+ if (mask & SD_BUS_CREDS_EXE) {
+ c->exe = strdup(item->str);
+ if (!c->exe) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->mask |= SD_BUS_CREDS_EXE;
+ }
+ break;
+
+ case KDBUS_ITEM_CMDLINE:
+ if (mask & SD_BUS_CREDS_CMDLINE) {
+ c->cmdline_size = item->size - KDBUS_ITEM_HEADER_SIZE;
+ c->cmdline = memdup(item->data, c->cmdline_size);
+ if (!c->cmdline) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->mask |= SD_BUS_CREDS_CMDLINE;
+ }
+ break;
+
+ case KDBUS_ITEM_CGROUP:
+ m = (SD_BUS_CREDS_CGROUP | SD_BUS_CREDS_UNIT |
+ SD_BUS_CREDS_USER_UNIT | SD_BUS_CREDS_SLICE |
+ SD_BUS_CREDS_SESSION | SD_BUS_CREDS_OWNER_UID) & mask;
+
+ if (m) {
+ c->cgroup = strdup(item->str);
+ if (!c->cgroup) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!bus->cgroup_root) {
+ r = cg_get_root_path(&bus->cgroup_root);
+ if (r < 0)
+ goto fail;
+ }
+
+ c->cgroup_root = strdup(bus->cgroup_root);
+ if (!c->cgroup_root) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->mask |= m;
+ }
+ break;
+
+ case KDBUS_ITEM_CAPS:
+ m = (SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_PERMITTED_CAPS |
+ SD_BUS_CREDS_INHERITABLE_CAPS | SD_BUS_CREDS_BOUNDING_CAPS) & mask;
+
+ if (m) {
+ c->capability_size = item->size - KDBUS_ITEM_HEADER_SIZE;
+ c->capability = memdup(item->data, c->capability_size);
+ if (!c->capability) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->mask |= m;
+ }
+ break;
+
+ case KDBUS_ITEM_SECLABEL:
+ if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
+ c->label = strdup(item->str);
+ if (!c->label) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
+ }
+ break;
+
+ case KDBUS_ITEM_AUDIT:
+ m = (SD_BUS_CREDS_AUDIT_SESSION_ID | SD_BUS_CREDS_AUDIT_LOGIN_UID) & mask;
+
+ if (m) {
+ c->audit_session_id = item->audit.sessionid;
+ c->audit_login_uid = item->audit.loginuid;
+ c->mask |= m;
+ }
+ break;
+
+ case KDBUS_ITEM_NAME:
+ if ((mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) && service_name_is_valid(item->name.name)) {
+ r = strv_extend(&c->well_known_names, item->name.name);
+ if (r < 0)
+ goto fail;
+
+ c->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES;
+ }
+ break;
+ }
+ }
+
+ if (creds) {
+ *creds = c;
+ c = NULL;
+ }
+
+ r = 0;
+
+fail:
+ ioctl(bus->input_fd, KDBUS_CMD_FREE, &cmd->offset);
+ return r;
+}
+
+static int bus_get_owner_dbus1(
+ sd_bus *bus,
+ const char *name,
+ uint64_t mask,
+ sd_bus_creds **creds) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *reply_unique = NULL, *reply = NULL;
+ _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
+ const char *unique = NULL;
+ pid_t pid = 0;
+ int r;
+
+ /* Only query the owner if the caller wants to know it or if
+ * the caller just wants to check whether a name exists */
+ if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) {
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetNameOwner",
+ NULL,
+ &reply_unique,
+ "s",
+ name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply_unique, "s", &unique);
+ if (r < 0)
+ return r;
+ }
+
+ if (mask != 0) {
+ c = bus_creds_new();
+ if (!c)
+ return -ENOMEM;
+
+ if ((mask & SD_BUS_CREDS_UNIQUE_NAME) && unique) {
+ c->unique_name = strdup(unique);
+ if (!c->unique_name)
+ return -ENOMEM;
+
+ c->mask |= SD_BUS_CREDS_UNIQUE_NAME;
+ }
+
+ if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_GID|
+ SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
+ SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|
+ SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
+ SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)) {
+ uint32_t u;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetConnectionUnixProcessID",
+ NULL,
+ &reply,
+ "s",
+ unique ? unique : name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "u", &u);
+ if (r < 0)
+ return r;
+
+ pid = u;
+ if (mask & SD_BUS_CREDS_PID) {
+ c->pid = u;
+ c->mask |= SD_BUS_CREDS_PID;
+ }
+
+ reply = sd_bus_message_unref(reply);
+ }
+
+ if (mask & SD_BUS_CREDS_UID) {
+ uint32_t u;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetConnectionUnixUser",
+ NULL,
+ &reply,
+ "s",
+ unique ? unique : name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "u", &u);
+ if (r < 0)
+ return r;
+
+ c->uid = u;
+ c->mask |= SD_BUS_CREDS_UID;
+
+ reply = sd_bus_message_unref(reply);
+ }
+
+ if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
+ const void *p;
+ size_t sz;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetConnectionSELinuxSecurityContext",
+ NULL,
+ &reply,
+ "s",
+ unique ? unique : name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_array(reply, 'y', &p, &sz);
+ if (r < 0)
+ return r;
+
+ c->label = strndup(p, sz);
+ if (!c->label)
+ return -ENOMEM;
+
+ c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
+ }
+
+ r = bus_creds_add_more(c, mask, pid, 0);
+ if (r < 0)
+ return r;
+ }
+
+ if (creds) {
+ *creds = c;
+ c = NULL;
+ }
+
+ return 0;
+}
+
+_public_ int sd_bus_get_owner(
+ sd_bus *bus,
+ const char *name,
+ uint64_t mask,
+ sd_bus_creds **creds) {
+
+ assert_return(bus, -EINVAL);
+ assert_return(name, -EINVAL);
+ assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP);
+ assert_return(mask == 0 || creds, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(service_name_is_valid(name), -EINVAL);
+ assert_return(bus->bus_client, -ENODATA);
+
+ if (bus->is_kernel)
+ return bus_get_owner_kdbus(bus, name, mask, creds);
+ else
+ return bus_get_owner_dbus1(bus, name, mask, creds);
+}
+
+static int add_name_change_match(sd_bus *bus,
+ uint64_t cookie,
+ const char *name,
+ const char *old_owner,
+ const char *new_owner) {
+
+ uint64_t name_id = KDBUS_MATCH_ID_ANY, old_owner_id = 0, new_owner_id = 0;
+ int is_name_id = -1, r;
+ struct kdbus_item *item;
+
+ assert(bus);
+
+ /* If we encounter a match that could match against
+ * NameOwnerChanged messages, then we need to create
+ * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE} and
+ * KDBUS_ITEM_ID_{ADD,REMOVE} matches for it, possibly
+ * multiple if the match is underspecified.
+ *
+ * The NameOwnerChanged signals take three parameters with
+ * unique or well-known names, but only some forms actually
+ * exist:
+ *
+ * WELLKNOWN, "", UNIQUE â KDBUS_ITEM_NAME_ADD
+ * WELLKNOWN, UNIQUE, "" â KDBUS_ITEM_NAME_REMOVE
+ * WELLKNOWN, UNIQUE, UNIQUE â KDBUS_ITEM_NAME_CHANGE
+ * UNIQUE, "", UNIQUE â KDBUS_ITEM_ID_ADD
+ * UNIQUE, UNIQUE, "" â KDBUS_ITEM_ID_REMOVE
+ *
+ * For the latter two the two unique names must be identical.
+ *
+ * */
+
+ if (name) {
+ is_name_id = bus_kernel_parse_unique_name(name, &name_id);
+ if (is_name_id < 0)
+ return 0;
+ }
+
+ if (!isempty(old_owner)) {
+ r = bus_kernel_parse_unique_name(old_owner, &old_owner_id);
+ if (r < 0)
+ return 0;
+ if (r == 0)
+ return 0;
+ if (is_name_id > 0 && old_owner_id != name_id)
+ return 0;
+ } else
+ old_owner_id = KDBUS_MATCH_ID_ANY;
+
+ if (!isempty(new_owner)) {
+ r = bus_kernel_parse_unique_name(new_owner, &new_owner_id);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 0;
+ if (is_name_id > 0 && new_owner_id != name_id)
+ return 0;
+ } else
+ new_owner_id = KDBUS_MATCH_ID_ANY;
+
+ if (is_name_id <= 0) {
+ struct kdbus_cmd_match *m;
+ size_t sz, l;
+
+ /* If the name argument is missing or is a well-known
+ * name, then add KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}
+ * matches for it */
+
+ l = name ? strlen(name) + 1 : 0;
+
+ sz = ALIGN8(offsetof(struct kdbus_cmd_match, items) +
+ offsetof(struct kdbus_item, name_change) +
+ offsetof(struct kdbus_notify_name_change, name) +
+ l);
+
+ m = alloca0(sz);
+ m->size = sz;
+ m->cookie = cookie;
+
+ item = m->items;
+ item->size =
+ offsetof(struct kdbus_item, name_change) +
+ offsetof(struct kdbus_notify_name_change, name) +
+ l;
+
+ item->name_change.old.id = old_owner_id;
+ item->name_change.new.id = new_owner_id;
+
+ if (name)
+ memcpy(item->name_change.name, name, l);
+
+ /* If the old name is unset or empty, then
+ * this can match against added names */
+ if (!old_owner || old_owner[0] == 0) {
+ item->type = KDBUS_ITEM_NAME_ADD;
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
+ if (r < 0)
+ return -errno;
+ }
+
+ /* If the new name is unset or empty, then
+ * this can match against removed names */
+ if (!new_owner || new_owner[0] == 0) {
+ item->type = KDBUS_ITEM_NAME_REMOVE;
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
+ if (r < 0)
+ return -errno;
+ }
+
+ /* The CHANGE match we need in either case, because
+ * what is reported as a name change by the kernel
+ * might just be an owner change between starter and
+ * normal clients. For userspace such a change should
+ * be considered a removal/addition, hence let's
+ * subscribe to this unconditionally. */
+ item->type = KDBUS_ITEM_NAME_CHANGE;
+ r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
+ if (r < 0)
+ return -errno;
+ }
+
+ if (is_name_id != 0) {
+ struct kdbus_cmd_match *m;
+ uint64_t sz;
+
+ /* If the name argument is missing or is a unique
+ * name, then add KDBUS_ITEM_ID_{ADD,REMOVE} matches
+ * for it */
+
+ sz = ALIGN8(offsetof(struct kdbus_cmd_match, items) +
+ offsetof(struct kdbus_item, id_change) +
+ sizeof(struct kdbus_notify_id_change));
+
+ m = alloca0(sz);
+ m->size = sz;
+ m->cookie = cookie;
+
+ item = m->items;
+ item->size =
+ offsetof(struct kdbus_item, id_change) +
+ sizeof(struct kdbus_notify_id_change);
+ item->id_change.id = name_id;
+
+ /* If the old name is unset or empty, then this can
+ * match against added ids */
+ if (!old_owner || old_owner[0] == 0) {
+ item->type = KDBUS_ITEM_ID_ADD;
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
+ if (r < 0)
+ return -errno;
+ }
+
+ /* If thew new name is unset or empty, then this can
+ * match against removed ids */
+ if (!new_owner || new_owner[0] == 0) {
+ item->type = KDBUS_ITEM_ID_REMOVE;
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
+ if (r < 0)
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+int bus_add_match_internal_kernel(
+ sd_bus *bus,
+ uint64_t id,
+ struct bus_match_component *components,
+ unsigned n_components,
+ uint64_t cookie) {
+
+ struct kdbus_cmd_match *m;
+ struct kdbus_item *item;
+ uint64_t bloom[BLOOM_SIZE/8];
+ size_t sz;
+ const char *sender = NULL;
+ size_t sender_length = 0;
+ uint64_t src_id = KDBUS_MATCH_ID_ANY;
+ bool using_bloom = false;
+ unsigned i;
+ bool matches_name_change = true;
+ const char *name_change_arg[3] = {};
+ int r;
+
+ assert(bus);
+
+ zero(bloom);
+
+ sz = ALIGN8(offsetof(struct kdbus_cmd_match, items));
+
+ for (i = 0; i < n_components; i++) {
+ struct bus_match_component *c = &components[i];
+
+ switch (c->type) {
+
+ case BUS_MATCH_SENDER:
+ if (!streq(c->value_str, "org.freedesktop.DBus"))
+ matches_name_change = false;
+
+ r = bus_kernel_parse_unique_name(c->value_str, &src_id);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ sz += ALIGN8(offsetof(struct kdbus_item, id) + sizeof(uint64_t));
+ else {
+ sender = c->value_str;
+ sender_length = strlen(sender);
+ sz += ALIGN8(offsetof(struct kdbus_item, str) + sender_length + 1);
+ }
+
+ break;
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ if (c->value_u8 != SD_BUS_MESSAGE_SIGNAL)
+ matches_name_change = false;
+
+ bloom_add_pair(bloom, "message-type", bus_message_type_to_string(c->value_u8));
+ using_bloom = true;
+ break;
+
+ case BUS_MATCH_INTERFACE:
+ if (!streq(c->value_str, "org.freedesktop.DBus"))
+ matches_name_change = false;
+
+ bloom_add_pair(bloom, "interface", c->value_str);
+ using_bloom = true;
+ break;
+
+ case BUS_MATCH_MEMBER:
+ if (!streq(c->value_str, "NameOwnerChanged"))
+ matches_name_change = false;
+
+ bloom_add_pair(bloom, "member", c->value_str);
+ using_bloom = true;
+ break;
+
+ case BUS_MATCH_PATH:
+ if (!streq(c->value_str, "/org/freedesktop/DBus"))
+ matches_name_change = false;
+
+ bloom_add_pair(bloom, "path", c->value_str);
+ using_bloom = true;
+ break;
+
+ case BUS_MATCH_PATH_NAMESPACE:
+ if (!streq(c->value_str, "/")) {
+ bloom_add_pair(bloom, "path-slash-prefix", c->value_str);
+ using_bloom = true;
+ }
+ break;
+
+ case BUS_MATCH_ARG...BUS_MATCH_ARG_LAST: {
+ char buf[sizeof("arg")-1 + 2 + 1];
+
+ if (c->type - BUS_MATCH_ARG < 3)
+ name_change_arg[c->type - BUS_MATCH_ARG] = c->value_str;
+
+ snprintf(buf, sizeof(buf), "arg%u", c->type - BUS_MATCH_ARG);
+ bloom_add_pair(bloom, buf, c->value_str);
+ using_bloom = true;
+ break;
+ }
+
+ case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST: {
+ char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")];
+
+ snprintf(buf, sizeof(buf), "arg%u-slash-prefix", c->type - BUS_MATCH_ARG_PATH);
+ bloom_add_pair(bloom, buf, c->value_str);
+ using_bloom = true;
+ break;
+ }
+
+ case BUS_MATCH_ARG_NAMESPACE...BUS_MATCH_ARG_NAMESPACE_LAST: {
+ char buf[sizeof("arg")-1 + 2 + sizeof("-dot-prefix")];
+
+ snprintf(buf, sizeof(buf), "arg%u-dot-prefix", c->type - BUS_MATCH_ARG_NAMESPACE);
+ bloom_add_pair(bloom, buf, c->value_str);
+ using_bloom = true;
+ break;
+ }
+
+ case BUS_MATCH_DESTINATION:
+ /* The bloom filter does not include
+ the destination, since it is only
+ available for broadcast messages
+ which do not carry a destination
+ since they are undirected. */
+ break;
+
+ case BUS_MATCH_ROOT:
+ case BUS_MATCH_VALUE:
+ case BUS_MATCH_LEAF:
+ case _BUS_MATCH_NODE_TYPE_MAX:
+ case _BUS_MATCH_NODE_TYPE_INVALID:
+ assert_not_reached("Invalid match type?");
+ }
+ }
+
+ if (using_bloom)
+ sz += ALIGN8(offsetof(struct kdbus_item, data64) + BLOOM_SIZE);
+
+ m = alloca0(sz);
+ m->size = sz;
+ m->cookie = cookie;
+ m->owner_id = id;
+
+ item = m->items;
+
+ if (src_id != KDBUS_MATCH_ID_ANY) {
+ item->size = offsetof(struct kdbus_item, id) + sizeof(uint64_t);
+ item->type = KDBUS_ITEM_ID;
+ item->id = src_id;
+ item = KDBUS_ITEM_NEXT(item);
+ }
+
+ if (using_bloom) {
+ item->size = offsetof(struct kdbus_item, data64) + BLOOM_SIZE;
+ item->type = KDBUS_ITEM_BLOOM;
+ memcpy(item->data64, bloom, BLOOM_SIZE);
+ item = KDBUS_ITEM_NEXT(item);
+ }
+
+ if (sender) {
+ item->size = offsetof(struct kdbus_item, str) + sender_length + 1;
+ item->type = KDBUS_ITEM_NAME;
+ memcpy(item->str, sender, sender_length + 1);
+ }
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
+ if (r < 0)
+ return -errno;
+
+ if (matches_name_change) {
+
+ /* If this match could theoretically match
+ * NameOwnerChanged messages, we need to
+ * install a second non-bloom filter explitly
+ * for it */
+
+ r = add_name_change_match(bus, cookie, name_change_arg[0], name_change_arg[1], name_change_arg[2]);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int bus_add_match_internal_dbus1(
+ sd_bus *bus,
+ const char *match) {
+
+ assert(bus);
+ assert(match);
+
+ return sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "AddMatch",
+ NULL,
+ NULL,
+ "s",
+ match);
+}
+
+int bus_add_match_internal(
+ sd_bus *bus,
+ const char *match,
+ struct bus_match_component *components,
+ unsigned n_components,
+ uint64_t cookie) {
+
+ assert(bus);
+ assert(match);
+
+ if (bus->is_kernel)
+ return bus_add_match_internal_kernel(bus, 0, components, n_components, cookie);
+ else
+ return bus_add_match_internal_dbus1(bus, match);
+}
+
+int bus_remove_match_internal_kernel(
+ sd_bus *bus,
+ uint64_t id,
+ uint64_t cookie) {
+
+ struct kdbus_cmd_match m;
+ int r;
+
+ assert(bus);
+
+ zero(m);
+ m.size = offsetof(struct kdbus_cmd_match, items);
+ m.cookie = cookie;
+ m.owner_id = id;
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_REMOVE, &m);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int bus_remove_match_internal_dbus1(
+ sd_bus *bus,
+ const char *match) {
+
+ assert(bus);
+ assert(match);
+
+ return sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "RemoveMatch",
+ NULL,
+ NULL,
+ "s",
+ match);
+}
+
+int bus_remove_match_internal(
+ sd_bus *bus,
+ const char *match,
+ uint64_t cookie) {
+
+ assert(bus);
+ assert(match);
+
+ if (bus->is_kernel)
+ return bus_remove_match_internal_kernel(bus, 0, cookie);
+ else
+ return bus_remove_match_internal_dbus1(bus, match);
+}
+
+_public_ int sd_bus_get_owner_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
+ const char *mid;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(name, -EINVAL);
+ assert_return(machine, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(service_name_is_valid(name), -EINVAL);
+
+ if (streq_ptr(name, bus->unique_name))
+ return sd_id128_get_machine(machine);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ name,
+ "/",
+ "org.freedesktop.DBus.Peer",
+ "GetMachineId", &m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_set_no_auto_start(m, true);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call(bus, m, 0, NULL, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "s", &mid);
+ if (r < 0)
+ return r;
+
+ return sd_id128_from_string(mid, machine);
+}
diff --git a/src/libsystemd/sd-bus/bus-control.h b/src/libsystemd/sd-bus/bus-control.h
new file mode 100644
index 0000000..b610bef
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-control.h
@@ -0,0 +1,30 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+int bus_add_match_internal(sd_bus *bus, const char *match, struct bus_match_component *components, unsigned n_components, uint64_t cookie);
+int bus_remove_match_internal(sd_bus *bus, const char *match, uint64_t cookie);
+
+int bus_add_match_internal_kernel(sd_bus *bus, uint64_t id, struct bus_match_component *components, unsigned n_components, uint64_t cookie);
+int bus_remove_match_internal_kernel(sd_bus *bus, uint64_t id, uint64_t cookie);
diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c
new file mode 100644
index 0000000..3964960
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-convenience.c
@@ -0,0 +1,442 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-signature.h"
+#include "bus-util.h"
+#include "bus-type.h"
+
+_public_ int sd_bus_emit_signal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *types, ...) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = sd_bus_message_new_signal(bus, path, interface, member, &m);
+ if (r < 0)
+ return r;
+
+ if (!isempty(types)) {
+ va_list ap;
+
+ va_start(ap, types);
+ r = bus_message_append_ap(m, types, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_send(bus, m, NULL);
+}
+
+_public_ int sd_bus_call_method(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_error *error,
+ sd_bus_message **reply,
+ const char *types, ...) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = sd_bus_message_new_method_call(bus, destination, path, interface, member, &m);
+ if (r < 0)
+ return r;
+
+ if (!isempty(types)) {
+ va_list ap;
+
+ va_start(ap, types);
+ r = bus_message_append_ap(m, types, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_call(bus, m, 0, error, reply);
+}
+
+_public_ int sd_bus_reply_method_return(
+ sd_bus_message *call,
+ const char *types, ...) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ assert_return(call, -EINVAL);
+ assert_return(call->sealed, -EPERM);
+ assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
+ assert_return(call->bus && BUS_IS_OPEN(call->bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(call->bus), -ECHILD);
+
+ if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return 0;
+
+ r = sd_bus_message_new_method_return(call, &m);
+ if (r < 0)
+ return r;
+
+ if (!isempty(types)) {
+ va_list ap;
+
+ va_start(ap, types);
+ r = bus_message_append_ap(m, types, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_send(call->bus, m, NULL);
+}
+
+_public_ int sd_bus_reply_method_error(
+ sd_bus_message *call,
+ const sd_bus_error *e) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ assert_return(call, -EINVAL);
+ assert_return(call->sealed, -EPERM);
+ assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
+ assert_return(sd_bus_error_is_set(e), -EINVAL);
+ assert_return(call->bus && BUS_IS_OPEN(call->bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(call->bus), -ECHILD);
+
+ if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return 0;
+
+ r = sd_bus_message_new_method_error(call, e, &m);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(call->bus, m, NULL);
+}
+
+_public_ int sd_bus_reply_method_errorf(
+ sd_bus_message *call,
+ const char *name,
+ const char *format,
+ ...) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ va_list ap;
+
+ assert_return(call, -EINVAL);
+ assert_return(call->sealed, -EPERM);
+ assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
+ assert_return(call->bus && BUS_IS_OPEN(call->bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(call->bus), -ECHILD);
+
+ if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return 0;
+
+ va_start(ap, format);
+ bus_error_setfv(&error, name, format, ap);
+ va_end(ap);
+
+ return sd_bus_reply_method_error(call, &error);
+}
+
+_public_ int sd_bus_reply_method_errno(
+ sd_bus_message *call,
+ int error,
+ const sd_bus_error *p) {
+
+ _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
+
+ assert_return(call, -EINVAL);
+ assert_return(call->sealed, -EPERM);
+ assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
+ assert_return(call->bus && BUS_IS_OPEN(call->bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(call->bus), -ECHILD);
+
+ if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return 0;
+
+ if (sd_bus_error_is_set(p))
+ return sd_bus_reply_method_error(call, p);
+
+ sd_bus_error_set_errno(&berror, error);
+
+ return sd_bus_reply_method_error(call, &berror);
+}
+
+_public_ int sd_bus_reply_method_errnof(
+ sd_bus_message *call,
+ int error,
+ const char *format,
+ ...) {
+
+ _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
+ va_list ap;
+
+ assert_return(call, -EINVAL);
+ assert_return(call->sealed, -EPERM);
+ assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
+ assert_return(call->bus && BUS_IS_OPEN(call->bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(call->bus), -ECHILD);
+
+ if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return 0;
+
+ va_start(ap, format);
+ bus_error_set_errnofv(&berror, error, format, ap);
+ va_end(ap);
+
+ return sd_bus_reply_method_error(call, &berror);
+}
+
+_public_ int sd_bus_get_property(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_error *error,
+ sd_bus_message **reply,
+ const char *type) {
+
+ sd_bus_message *rep = NULL;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
+ assert_return(member_name_is_valid(member), -EINVAL);
+ assert_return(reply, -EINVAL);
+ assert_return(signature_is_single(type, false), -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(rep, 'v', type);
+ if (r < 0) {
+ sd_bus_message_unref(rep);
+ return r;
+ }
+
+ *reply = rep;
+ return 0;
+}
+
+_public_ int sd_bus_get_property_trivial(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_error *error,
+ char type, void *ptr) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
+ assert_return(member_name_is_valid(member), -EINVAL);
+ assert_return(bus_type_is_trivial(type), -EINVAL);
+ assert_return(ptr, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type));
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_basic(reply, type, ptr);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+_public_ int sd_bus_get_property_string(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_error *error,
+ char **ret) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ const char *s;
+ char *n;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
+ assert_return(member_name_is_valid(member), -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(reply, 'v', "s");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_basic(reply, 's', &s);
+ if (r < 0)
+ return r;
+
+ n = strdup(s);
+ if (!n)
+ return -ENOMEM;
+
+ *ret = n;
+ return 0;
+}
+
+_public_ int sd_bus_get_property_strv(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_error *error,
+ char ***ret) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
+ assert_return(member_name_is_valid(member), -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(reply, 'v', NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(reply, ret);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+_public_ int sd_bus_set_property(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_error *error,
+ const char *type, ...) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ va_list ap;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
+ assert_return(member_name_is_valid(member), -EINVAL);
+ assert_return(signature_is_single(type, false), -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = sd_bus_message_new_method_call(bus, destination, path, "org.freedesktop.DBus.Properties", "Set", &m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "ss", strempty(interface), member);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(m, 'v', type);
+ if (r < 0)
+ return r;
+
+ va_start(ap, type);
+ r = bus_message_append_ap(m, type, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ return sd_bus_call(bus, m, 0, error, NULL);
+}
+
+_public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) {
+ sd_bus_creds *c;
+
+ assert_return(call, -EINVAL);
+ assert_return(call->sealed, -EPERM);
+ assert_return(call->bus && BUS_IS_OPEN(call->bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(call->bus), -ECHILD);
+
+ c = sd_bus_message_get_creds(call);
+
+ /* All data we need? */
+ if (c && (mask & ~c->mask) == 0) {
+ *creds = sd_bus_creds_ref(c);
+ return 0;
+ }
+
+ /* No data passed? Or not enough data passed to retrieve the missing bits? */
+ if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
+ /* We couldn't read anything from the call, let's try
+ * to get it from the sender or peer */
+
+ if (call->sender)
+ return sd_bus_get_owner(call->bus, call->sender, mask, creds);
+ else
+ return sd_bus_get_peer_creds(call->bus, mask, creds);
+ }
+
+ return bus_creds_extend_by_pid(c, mask, creds);
+}
diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c
new file mode 100644
index 0000000..52e55fc
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-creds.c
@@ -0,0 +1,904 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+
+#include "util.h"
+#include "cgroup-util.h"
+#include "fileio.h"
+#include "audit.h"
+#include "bus-message.h"
+#include "bus-util.h"
+#include "time-util.h"
+#include "strv.h"
+#include "bus-creds.h"
+
+enum {
+ CAP_OFFSET_INHERITABLE = 0,
+ CAP_OFFSET_PERMITTED = 1,
+ CAP_OFFSET_EFFECTIVE = 2,
+ CAP_OFFSET_BOUNDING = 3
+};
+
+void bus_creds_done(sd_bus_creds *c) {
+ assert(c);
+
+ /* For internal bus cred structures that are allocated by
+ * something else */
+
+ free(c->session);
+ free(c->unit);
+ free(c->user_unit);
+ free(c->slice);
+
+ strv_free(c->cmdline_array);
+ strv_free(c->well_known_names);
+}
+
+_public_ sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c) {
+ assert_return(c, NULL);
+
+ if (c->allocated) {
+ assert(c->n_ref > 0);
+ c->n_ref++;
+ } else {
+ sd_bus_message *m;
+
+ /* If this is an embedded creds structure, then
+ * forward ref counting to the message */
+ m = container_of(c, sd_bus_message, creds);
+ sd_bus_message_ref(m);
+ }
+
+ return c;
+}
+
+_public_ sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c) {
+
+ if (!c)
+ return NULL;
+
+ if (c->allocated) {
+ assert(c->n_ref > 0);
+ c->n_ref--;
+
+ if (c->n_ref == 0) {
+ bus_creds_done(c);
+
+ free(c->comm);
+ free(c->tid_comm);
+ free(c->exe);
+ free(c->cmdline);
+ free(c->cgroup);
+ free(c->capability);
+ free(c->label);
+ free(c->unique_name);
+ free(c->cgroup_root);
+ free(c);
+ }
+ } else {
+ sd_bus_message *m;
+
+ m = container_of(c, sd_bus_message, creds);
+ sd_bus_message_unref(m);
+ }
+
+
+ return NULL;
+}
+
+_public_ uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c) {
+ assert_return(c, 0);
+
+ return c->mask;
+}
+
+sd_bus_creds* bus_creds_new(void) {
+ sd_bus_creds *c;
+
+ c = new0(sd_bus_creds, 1);
+ if (!c)
+ return NULL;
+
+ c->allocated = true;
+ c->n_ref = 1;
+ return c;
+}
+
+_public_ int sd_bus_creds_new_from_pid(pid_t pid, uint64_t mask, sd_bus_creds **ret) {
+ sd_bus_creds *c;
+ int r;
+
+ assert_return(pid >= 0, -EINVAL);
+ assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP);
+ assert_return(ret, -EINVAL);
+
+ if (pid == 0)
+ pid = getpid();
+
+ c = bus_creds_new();
+ if (!c)
+ return -ENOMEM;
+
+ r = bus_creds_add_more(c, mask, pid, 0);
+ if (r < 0) {
+ sd_bus_creds_unref(c);
+ return r;
+ }
+
+ /* Check if the process existed at all, in case we haven't
+ * figured that out already */
+ if (kill(pid, 0) < 0 && errno == ESRCH) {
+ sd_bus_creds_unref(c);
+ return -ESRCH;
+ }
+
+ *ret = c;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) {
+ assert_return(c, -EINVAL);
+ assert_return(uid, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_UID))
+ return -ENODATA;
+
+ *uid = c->uid;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) {
+ assert_return(c, -EINVAL);
+ assert_return(gid, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_UID))
+ return -ENODATA;
+
+ *gid = c->gid;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) {
+ assert_return(c, -EINVAL);
+ assert_return(pid, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_PID))
+ return -ENODATA;
+
+ assert(c->pid > 0);
+ *pid = c->pid;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid) {
+ assert_return(c, -EINVAL);
+ assert_return(tid, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_TID))
+ return -ENODATA;
+
+ assert(c->tid > 0);
+ *tid = c->tid;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_pid_starttime(sd_bus_creds *c, uint64_t *usec) {
+ assert_return(c, -EINVAL);
+ assert_return(usec, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_PID_STARTTIME))
+ return -ENODATA;
+
+ assert(c->pid_starttime > 0);
+ *usec = c->pid_starttime;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **ret) {
+ assert_return(c, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_SELINUX_CONTEXT))
+ return -ENODATA;
+
+ assert(c->label);
+ *ret = c->label;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_comm(sd_bus_creds *c, const char **ret) {
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_COMM))
+ return -ENODATA;
+
+ assert(c->comm);
+ *ret = c->comm;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_tid_comm(sd_bus_creds *c, const char **ret) {
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_TID_COMM))
+ return -ENODATA;
+
+ assert(c->tid_comm);
+ *ret = c->tid_comm;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_exe(sd_bus_creds *c, const char **ret) {
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_EXE))
+ return -ENODATA;
+
+ assert(c->exe);
+ *ret = c->exe;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_cgroup(sd_bus_creds *c, const char **ret) {
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_CGROUP))
+ return -ENODATA;
+
+ assert(c->cgroup);
+ *ret = c->cgroup;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_unit(sd_bus_creds *c, const char **ret) {
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_UNIT))
+ return -ENODATA;
+
+ assert(c->cgroup);
+
+ if (!c->unit) {
+ const char *shifted;
+
+ r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
+ if (r < 0)
+ return r;
+
+ r = cg_path_get_unit(shifted, (char**) &c->unit);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = c->unit;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **ret) {
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_USER_UNIT))
+ return -ENODATA;
+
+ assert(c->cgroup);
+
+ if (!c->user_unit) {
+ const char *shifted;
+
+ r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
+ if (r < 0)
+ return r;
+
+ r = cg_path_get_user_unit(shifted, (char**) &c->user_unit);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = c->user_unit;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_slice(sd_bus_creds *c, const char **ret) {
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_SLICE))
+ return -ENODATA;
+
+ assert(c->cgroup);
+
+ if (!c->slice) {
+ const char *shifted;
+
+ r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
+ if (r < 0)
+ return r;
+
+ r = cg_path_get_slice(shifted, (char**) &c->slice);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = c->slice;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_session(sd_bus_creds *c, const char **ret) {
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_SESSION))
+ return -ENODATA;
+
+ assert(c->cgroup);
+
+ if (!c->session) {
+ const char *shifted;
+
+ r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
+ if (r < 0)
+ return r;
+
+ r = cg_path_get_session(shifted, (char**) &c->session);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = c->session;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid) {
+ const char *shifted;
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(uid, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_OWNER_UID))
+ return -ENODATA;
+
+ assert(c->cgroup);
+
+ r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_owner_uid(shifted, uid);
+}
+
+_public_ int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline) {
+ assert_return(c, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_CMDLINE))
+ return -ENODATA;
+
+ assert_return(c->cmdline, -ESRCH);
+ assert(c->cmdline);
+
+ if (!c->cmdline_array) {
+ c->cmdline_array = strv_parse_nulstr(c->cmdline, c->cmdline_size);
+ if (!c->cmdline_array)
+ return -ENOMEM;
+ }
+
+ *cmdline = c->cmdline_array;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid) {
+ assert_return(c, -EINVAL);
+ assert_return(sessionid, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_AUDIT_SESSION_ID))
+ return -ENODATA;
+
+ *sessionid = c->audit_session_id;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *uid) {
+ assert_return(c, -EINVAL);
+ assert_return(uid, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_AUDIT_LOGIN_UID))
+ return -ENODATA;
+
+ *uid = c->audit_login_uid;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **unique_name) {
+ assert_return(c, -EINVAL);
+ assert_return(unique_name, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_UNIQUE_NAME))
+ return -ENODATA;
+
+ *unique_name = c->unique_name;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***well_known_names) {
+ assert_return(c, -EINVAL);
+ assert_return(well_known_names, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_WELL_KNOWN_NAMES))
+ return -ENODATA;
+
+ *well_known_names = c->well_known_names;
+ return 0;
+}
+
+static int has_cap(sd_bus_creds *c, unsigned offset, int capability) {
+ size_t sz;
+
+ assert(c);
+ assert(c->capability);
+
+ sz = c->capability_size / 4;
+ if ((size_t) capability >= sz*8)
+ return 0;
+
+ return !!(c->capability[offset * sz + (capability / 8)] & (1 << (capability % 8)));
+}
+
+_public_ int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability) {
+ assert_return(c, -EINVAL);
+ assert_return(capability >= 0, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_EFFECTIVE_CAPS))
+ return -ENODATA;
+
+ return has_cap(c, CAP_OFFSET_EFFECTIVE, capability);
+}
+
+_public_ int sd_bus_creds_has_permitted_cap(sd_bus_creds *c, int capability) {
+ assert_return(c, -EINVAL);
+ assert_return(capability >= 0, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_PERMITTED_CAPS))
+ return -ENODATA;
+
+ return has_cap(c, CAP_OFFSET_PERMITTED, capability);
+}
+
+_public_ int sd_bus_creds_has_inheritable_cap(sd_bus_creds *c, int capability) {
+ assert_return(c, -EINVAL);
+ assert_return(capability >= 0, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_INHERITABLE_CAPS))
+ return -ENODATA;
+
+ return has_cap(c, CAP_OFFSET_INHERITABLE, capability);
+}
+
+_public_ int sd_bus_creds_has_bounding_cap(sd_bus_creds *c, int capability) {
+ assert_return(c, -EINVAL);
+ assert_return(capability >= 0, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_BOUNDING_CAPS))
+ return -ENODATA;
+
+ return has_cap(c, CAP_OFFSET_BOUNDING, capability);
+}
+
+static int parse_caps(sd_bus_creds *c, unsigned offset, const char *p) {
+ size_t sz;
+ unsigned i;
+
+ assert(c);
+ assert(p);
+
+ p += strspn(p, WHITESPACE);
+
+ sz = strlen(p);
+ if (sz % 2 != 0)
+ return -EINVAL;
+
+ sz /= 2;
+ if (!c->capability) {
+ c->capability = new0(uint8_t, sz * 4);
+ if (!c->capability)
+ return -ENOMEM;
+
+ c->capability_size = sz * 4;
+ }
+
+ for (i = 0; i < sz; i ++) {
+ int x, y;
+
+ x = unhexchar(p[i*2]);
+ y = unhexchar(p[i*2+1]);
+
+ if (x < 0 || y < 0)
+ return -EINVAL;
+
+ c->capability[offset * sz + (sz - i - 1)] = (uint8_t) x << 4 | (uint8_t) y;
+ }
+
+ return 0;
+}
+
+int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
+ uint64_t missing;
+ int r;
+
+ assert(c);
+ assert(c->allocated);
+
+ missing = mask & ~c->mask;
+ if (missing == 0)
+ return 0;
+
+ /* Try to retrieve PID from creds if it wasn't passed to us */
+ if (pid <= 0 && (c->mask & SD_BUS_CREDS_PID))
+ pid = c->pid;
+
+ if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID))
+ tid = c->pid;
+
+ /* Without pid we cannot do much... */
+ if (pid <= 0)
+ return 0;
+
+ if (missing & (SD_BUS_CREDS_UID | SD_BUS_CREDS_GID |
+ SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_INHERITABLE_CAPS |
+ SD_BUS_CREDS_PERMITTED_CAPS | SD_BUS_CREDS_BOUNDING_CAPS)) {
+
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ const char *p;
+
+ p = procfs_file_alloca(pid, "status");
+
+ f = fopen(p, "re");
+ if (!f)
+ return errno == ENOENT ? -ESRCH : -errno;
+
+ FOREACH_LINE(line, f, return -errno) {
+ truncate_nl(line);
+
+ if (missing & SD_BUS_CREDS_UID) {
+ p = startswith(line, "Uid:");
+ if (p) {
+ unsigned long uid;
+
+ p += strspn(p, WHITESPACE);
+ if (sscanf(p, "%lu", &uid) != 1)
+ return -EIO;
+
+ c->uid = (uid_t) uid;
+ c->mask |= SD_BUS_CREDS_UID;
+ continue;
+ }
+ }
+
+ if (missing & SD_BUS_CREDS_GID) {
+ p = startswith(line, "Gid:");
+ if (p) {
+ unsigned long gid;
+
+ p += strspn(p, WHITESPACE);
+ if (sscanf(p, "%lu", &gid) != 1)
+ return -EIO;
+
+ c->gid = (uid_t) gid;
+ c->mask |= SD_BUS_CREDS_GID;
+ continue;
+ }
+ }
+
+ if (missing & SD_BUS_CREDS_EFFECTIVE_CAPS) {
+ p = startswith(line, "CapEff:");
+ if (p) {
+ r = parse_caps(c, CAP_OFFSET_EFFECTIVE, p);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_EFFECTIVE_CAPS;
+ continue;
+ }
+ }
+
+ if (missing & SD_BUS_CREDS_PERMITTED_CAPS) {
+ p = startswith(line, "CapPrm:");
+ if (p) {
+ r = parse_caps(c, CAP_OFFSET_PERMITTED, p);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_PERMITTED_CAPS;
+ continue;
+ }
+ }
+
+ if (missing & SD_BUS_CREDS_INHERITABLE_CAPS) {
+ p = startswith(line, "CapInh:");
+ if (p) {
+ r = parse_caps(c, CAP_OFFSET_INHERITABLE, p);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_INHERITABLE_CAPS;
+ continue;
+ }
+ }
+
+ if (missing & SD_BUS_CREDS_BOUNDING_CAPS) {
+ p = startswith(line, "CapBnd:");
+ if (p) {
+ r = parse_caps(c, CAP_OFFSET_BOUNDING, p);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_BOUNDING_CAPS;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (missing & (SD_BUS_CREDS_PID_STARTTIME)) {
+ unsigned long long st;
+
+ r = get_starttime_of_pid(pid, &st);
+ if (r < 0)
+ return r;
+
+ c->pid_starttime = ((usec_t) st * USEC_PER_SEC) / (usec_t) sysconf(_SC_CLK_TCK);
+ c->mask |= SD_BUS_CREDS_PID_STARTTIME;
+ }
+
+ if (missing & SD_BUS_CREDS_SELINUX_CONTEXT) {
+ const char *p;
+
+ p = procfs_file_alloca(pid, "attr/current");
+ r = read_one_line_file(p, &c->label);
+ if (r < 0 && r != -ENOENT && r != -EINVAL)
+ return r;
+ else if (r >= 0)
+ c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
+ }
+
+ if (missing & SD_BUS_CREDS_COMM) {
+ r = get_process_comm(pid, &c->comm);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_COMM;
+ }
+
+ if (missing & SD_BUS_CREDS_EXE) {
+ r = get_process_exe(pid, &c->exe);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_EXE;
+ }
+
+ if (missing & SD_BUS_CREDS_CMDLINE) {
+ const char *p;
+
+ p = procfs_file_alloca(pid, "cmdline");
+ r = read_full_file(p, &c->cmdline, &c->cmdline_size);
+ if (r < 0)
+ return r;
+
+ if (c->cmdline_size == 0) {
+ free(c->cmdline);
+ c->cmdline = NULL;
+ } else
+ c->mask |= SD_BUS_CREDS_CMDLINE;
+ }
+
+ if (tid > 0 && (missing & SD_BUS_CREDS_TID_COMM)) {
+ _cleanup_free_ char *p = NULL;
+
+ if (asprintf(&p, "/proc/%lu/task/%lu/comm", (unsigned long) pid, (unsigned long) tid) < 0)
+ return -ENOMEM;
+
+ r = read_one_line_file(p, &c->tid_comm);
+ if (r < 0)
+ return r == -ENOENT ? -ESRCH : r;
+
+ c->mask |= SD_BUS_CREDS_TID_COMM;
+ }
+
+ if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) {
+
+ r = cg_pid_get_path(NULL, pid, &c->cgroup);
+ if (r < 0)
+ return r;
+
+ r = cg_get_root_path(&c->cgroup_root);
+ if (r < 0)
+ return r;
+
+ c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID);
+ }
+
+ if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) {
+ r = audit_session_from_pid(pid, &c->audit_session_id);
+ if (r < 0 && r != -ENOTSUP && r != -ENXIO && r != -ENOENT)
+ return r;
+ else if (r >= 0)
+ c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID;
+ }
+
+ if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
+ r = audit_loginuid_from_pid(pid, &c->audit_login_uid);
+ if (r < 0 && r != -ENOTSUP && r != -ENXIO && r != -ENOENT)
+ return r;
+ else if (r >= 0)
+ c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
+ }
+
+ return 0;
+}
+
+int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *n = NULL;
+ int r;
+
+ assert(c);
+ assert(ret);
+
+ if ((mask & ~c->mask) == 0) {
+ /* There's already all data we need. */
+
+ *ret = sd_bus_creds_ref(c);
+ return 0;
+ }
+
+ n = bus_creds_new();
+ if (!n)
+ return -ENOMEM;
+
+ /* Copy the original data over */
+
+ if (c->mask & mask & SD_BUS_CREDS_UID) {
+ n->uid = c->uid;
+ n->mask |= SD_BUS_CREDS_UID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_GID) {
+ n->gid = c->gid;
+ n->mask |= SD_BUS_CREDS_GID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_PID) {
+ n->pid = c->pid;
+ n->mask |= SD_BUS_CREDS_PID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_TID) {
+ n->tid = c->tid;
+ n->mask |= SD_BUS_CREDS_TID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_PID_STARTTIME) {
+ n->pid_starttime = c->pid_starttime;
+ n->mask |= SD_BUS_CREDS_PID_STARTTIME;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_COMM) {
+ n->comm = strdup(c->comm);
+ if (!n->comm)
+ return -ENOMEM;
+
+ n->mask |= SD_BUS_CREDS_COMM;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_TID_COMM) {
+ n->tid_comm = strdup(c->tid_comm);
+ if (!n->tid_comm)
+ return -ENOMEM;
+
+ n->mask |= SD_BUS_CREDS_TID_COMM;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_EXE) {
+ n->exe = strdup(c->exe);
+ if (!n->exe)
+ return -ENOMEM;
+
+ n->mask |= SD_BUS_CREDS_EXE;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_CMDLINE) {
+ n->cmdline = memdup(c->cmdline, c->cmdline_size);
+ if (!n->cmdline)
+ return -ENOMEM;
+
+ n->cmdline_size = c->cmdline_size;
+ n->mask |= SD_BUS_CREDS_CMDLINE;
+ }
+
+ if (c->mask & mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_OWNER_UID)) {
+ n->cgroup = strdup(c->cgroup);
+ if (!n->cgroup)
+ return -ENOMEM;
+
+ n->cgroup_root = strdup(c->cgroup_root);
+ if (!n->cgroup_root)
+ return -ENOMEM;
+
+ n->mask |= mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_OWNER_UID);
+ }
+
+ if (c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) {
+ n->capability = memdup(c->capability, c->capability_size);
+ if (!n->capability)
+ return -ENOMEM;
+
+ n->capability_size = c->capability_size;
+ n->mask |= c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS);
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_AUDIT_SESSION_ID) {
+ n->audit_session_id = c->audit_session_id;
+ n->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
+ n->audit_login_uid = c->audit_login_uid;
+ n->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_UNIQUE_NAME) {
+ n->unique_name = strdup(c->unique_name);
+ if (!n->unique_name)
+ return -ENOMEM;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
+ n->well_known_names = strv_copy(c->well_known_names);
+ if (!n->well_known_names)
+ return -ENOMEM;
+ }
+
+ /* Get more data */
+
+ r = bus_creds_add_more(n, mask,
+ c->mask & SD_BUS_CREDS_PID ? c->pid : 0,
+ c->mask & SD_BUS_CREDS_TID ? c->tid : 0);
+ if (r < 0)
+ return r;
+
+ *ret = n;
+ n = NULL;
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/bus-creds.h b/src/libsystemd/sd-bus/bus-creds.h
new file mode 100644
index 0000000..d8b4aca
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-creds.h
@@ -0,0 +1,75 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "sd-bus.h"
+#include "time-util.h"
+
+struct sd_bus_creds {
+ bool allocated;
+ unsigned n_ref;
+ uint64_t mask;
+
+ uid_t uid;
+ gid_t gid;
+ pid_t pid;
+ usec_t pid_starttime;
+ pid_t tid;
+
+ char *comm;
+ char *tid_comm;
+ char *exe;
+
+ char *cmdline;
+ size_t cmdline_size;
+ char **cmdline_array;
+
+ char *cgroup;
+ char *session;
+ char *unit;
+ char *user_unit;
+ char *slice;
+
+ uint8_t *capability;
+ size_t capability_size;
+
+ uint32_t audit_session_id;
+ uid_t audit_login_uid;
+
+ char *label;
+
+ char *unique_name;
+
+ char **well_known_names;
+
+ char *cgroup_root;
+};
+
+sd_bus_creds* bus_creds_new(void);
+
+void bus_creds_done(sd_bus_creds *c);
+
+int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid);
+
+int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret);
diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c
new file mode 100644
index 0000000..78e7597
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-dump.c
@@ -0,0 +1,417 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "capability.h"
+#include "strv.h"
+#include "audit.h"
+
+#include "bus-message.h"
+#include "bus-internal.h"
+#include "bus-type.h"
+#include "bus-dump.h"
+
+static char *indent(unsigned level) {
+ char *p;
+
+ p = new(char, 2 + level + 1);
+ if (!p)
+ return NULL;
+
+ p[0] = p[1] = ' ';
+ memset(p + 2, '\t', level);
+ p[2 + level] = 0;
+
+ return p;
+}
+
+int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) {
+ unsigned level = 1;
+ int r;
+
+ assert(m);
+
+ if (!f)
+ f = stdout;
+
+ if (with_header) {
+ fprintf(f,
+ "%s%s%sType=%s%s%s Endian=%c Flags=%u Version=%u",
+ m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() :
+ m->header->type == SD_BUS_MESSAGE_METHOD_RETURN ? ansi_highlight_green() :
+ m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", draw_special_char(DRAW_TRIANGULAR_BULLET), ansi_highlight_off(),
+ ansi_highlight(), bus_message_type_to_string(m->header->type), ansi_highlight_off(),
+ m->header->endian,
+ m->header->flags,
+ m->header->version);
+
+ /* Display synthetic message serial number in a more readable
+ * format than (uint32_t) -1 */
+ if (BUS_MESSAGE_COOKIE(m) == 0xFFFFFFFFULL)
+ fprintf(f, " Cookie=-1");
+ else
+ fprintf(f, " Cookie=%lu", (unsigned long) BUS_MESSAGE_COOKIE(m));
+
+ if (m->reply_cookie != 0)
+ fprintf(f, " ReplyCookie=%lu", (unsigned long) m->reply_cookie);
+
+ fputs("\n", f);
+
+ if (m->sender)
+ fprintf(f, " Sender=%s%s%s", ansi_highlight(), m->sender, ansi_highlight_off());
+ if (m->destination)
+ fprintf(f, " Destination=%s%s%s", ansi_highlight(), m->destination, ansi_highlight_off());
+ if (m->path)
+ fprintf(f, " Path=%s%s%s", ansi_highlight(), m->path, ansi_highlight_off());
+ if (m->interface)
+ fprintf(f, " Interface=%s%s%s", ansi_highlight(), m->interface, ansi_highlight_off());
+ if (m->member)
+ fprintf(f, " Member=%s%s%s", ansi_highlight(), m->member, ansi_highlight_off());
+
+ if (m->sender || m->destination || m->path || m->interface || m->member)
+ fputs("\n", f);
+
+ if (sd_bus_error_is_set(&m->error))
+ fprintf(f,
+ " ErrorName=%s%s%s"
+ " ErrorMessage=%s\"%s\"%s\n",
+ ansi_highlight_red(), strna(m->error.name), ansi_highlight_off(),
+ ansi_highlight_red(), strna(m->error.message), ansi_highlight_off());
+
+ if (m->monotonic != 0)
+ fprintf(f, " Monotonic=%llu", (unsigned long long) m->monotonic);
+ if (m->realtime != 0)
+ fprintf(f, " Realtime=%llu", (unsigned long long) m->realtime);
+
+ if (m->monotonic != 0 || m->realtime != 0)
+ fputs("\n", f);
+
+ bus_creds_dump(&m->creds, f);
+ }
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0) {
+ log_error("Failed to rewind: %s", strerror(-r));
+ return r;
+ }
+
+ fprintf(f, " MESSAGE \"%s\" {\n", strempty(m->root_container.signature));
+
+ for (;;) {
+ _cleanup_free_ char *prefix = NULL;
+ const char *contents = NULL;
+ char type;
+ union {
+ uint8_t u8;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ int32_t s32;
+ uint64_t u64;
+ int64_t s64;
+ double d64;
+ const char *string;
+ int i;
+ } basic;
+
+ r = sd_bus_message_peek_type(m, &type, &contents);
+ if (r < 0) {
+ log_error("Failed to peek type: %s", strerror(-r));
+ return r;
+ }
+
+ if (r == 0) {
+ if (level <= 1)
+ break;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0) {
+ log_error("Failed to exit container: %s", strerror(-r));
+ return r;
+ }
+
+ level--;
+
+ prefix = indent(level);
+ if (!prefix)
+ return log_oom();
+
+ fprintf(f, "%s};\n", prefix);
+ continue;
+ }
+
+ prefix = indent(level);
+ if (!prefix)
+ return log_oom();
+
+ if (bus_type_is_container(type) > 0) {
+ r = sd_bus_message_enter_container(m, type, contents);
+ if (r < 0) {
+ log_error("Failed to enter container: %s", strerror(-r));
+ return r;
+ }
+
+ if (type == SD_BUS_TYPE_ARRAY)
+ fprintf(f, "%sARRAY \"%s\" {\n", prefix, contents);
+ else if (type == SD_BUS_TYPE_VARIANT)
+ fprintf(f, "%sVARIANT \"%s\" {\n", prefix, contents);
+ else if (type == SD_BUS_TYPE_STRUCT)
+ fprintf(f, "%sSTRUCT \"%s\" {\n", prefix, contents);
+ else if (type == SD_BUS_TYPE_DICT_ENTRY)
+ fprintf(f, "%sDICT_ENTRY \"%s\" {\n", prefix, contents);
+
+ level ++;
+
+ continue;
+ }
+
+ r = sd_bus_message_read_basic(m, type, &basic);
+ if (r < 0) {
+ log_error("Failed to get basic: %s", strerror(-r));
+ return r;
+ }
+
+ assert(r > 0);
+
+ switch (type) {
+
+ case SD_BUS_TYPE_BYTE:
+ fprintf(f, "%sBYTE %s%u%s;\n", prefix, ansi_highlight(), basic.u8, ansi_highlight_off());
+ break;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ fprintf(f, "%sBOOLEAN %s%s%s;\n", prefix, ansi_highlight(), true_false(basic.i), ansi_highlight_off());
+ break;
+
+ case SD_BUS_TYPE_INT16:
+ fprintf(f, "%sINT16 %s%i%s;\n", prefix, ansi_highlight(), basic.s16, ansi_highlight_off());
+ break;
+
+ case SD_BUS_TYPE_UINT16:
+ fprintf(f, "%sUINT16 %s%u%s;\n", prefix, ansi_highlight(), basic.u16, ansi_highlight_off());
+ break;
+
+ case SD_BUS_TYPE_INT32:
+ fprintf(f, "%sINT32 %s%i%s;\n", prefix, ansi_highlight(), basic.s32, ansi_highlight_off());
+ break;
+
+ case SD_BUS_TYPE_UINT32:
+ fprintf(f, "%sUINT32 %s%u%s;\n", prefix, ansi_highlight(), basic.u32, ansi_highlight_off());
+ break;
+
+ case SD_BUS_TYPE_INT64:
+ fprintf(f, "%sINT64 %s%lli%s;\n", prefix, ansi_highlight(), (long long) basic.s64, ansi_highlight_off());
+ break;
+
+ case SD_BUS_TYPE_UINT64:
+ fprintf(f, "%sUINT64 %s%llu%s;\n", prefix, ansi_highlight(), (unsigned long long) basic.u64, ansi_highlight_off());
+ break;
+
+ case SD_BUS_TYPE_DOUBLE:
+ fprintf(f, "%sDOUBLE %s%g%s;\n", prefix, ansi_highlight(), basic.d64, ansi_highlight_off());
+ break;
+
+ case SD_BUS_TYPE_STRING:
+ fprintf(f, "%sSTRING \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_highlight_off());
+ break;
+
+ case SD_BUS_TYPE_OBJECT_PATH:
+ fprintf(f, "%sOBJECT_PATH \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_highlight_off());
+ break;
+
+ case SD_BUS_TYPE_SIGNATURE:
+ fprintf(f, "%sSIGNATURE \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_highlight_off());
+ break;
+
+ case SD_BUS_TYPE_UNIX_FD:
+ fprintf(f, "%sUNIX_FD %s%i%s;\n", prefix, ansi_highlight(), basic.i, ansi_highlight_off());
+ break;
+
+ default:
+ assert_not_reached("Unknown basic type.");
+ }
+ }
+
+ fprintf(f, " };\n\n");
+ return 0;
+}
+
+static void dump_capabilities(
+ sd_bus_creds *c,
+ FILE *f,
+ const char *name,
+ int (*has)(sd_bus_creds *c, int capability)) {
+
+ unsigned long i, last_cap;
+ unsigned n = 0;
+ int r;
+
+ assert(c);
+ assert(f);
+ assert(name);
+ assert(has);
+
+ i = 0;
+ r = has(c, i);
+ if (r < 0)
+ return;
+
+ fprintf(f, " %s=", name);
+ last_cap = cap_last_cap();
+
+ for (;;) {
+ if (r > 0) {
+ _cleanup_cap_free_charp_ char *t;
+
+ if (n > 0)
+ fputc(' ', f);
+ if (n % 4 == 3)
+ fputs("\n ", f);
+
+ t = cap_to_name(i);
+ fprintf(f, "%s", t);
+ n++;
+ }
+
+ i++;
+
+ if (i > last_cap)
+ break;
+
+ r = has(c, i);
+ }
+
+ fputs("\n", f);
+}
+
+int bus_creds_dump(sd_bus_creds *c, FILE *f) {
+ bool audit_sessionid_is_set = false, audit_loginuid_is_set = false;
+ const char *u = NULL, *uu = NULL, *s = NULL, *sl = NULL;
+ uid_t owner, audit_loginuid;
+ uint32_t audit_sessionid;
+ char **cmdline = NULL, **well_known = NULL;
+ int r;
+
+ assert(c);
+
+ if (!f)
+ f = stdout;
+
+ if (c->mask & SD_BUS_CREDS_PID)
+ fprintf(f, " PID=%lu", (unsigned long) c->pid);
+ if (c->mask & SD_BUS_CREDS_PID_STARTTIME)
+ fprintf(f, " PIDStartTime=%llu", (unsigned long long) c->pid_starttime);
+ if (c->mask & SD_BUS_CREDS_TID)
+ fprintf(f, " TID=%lu", (unsigned long) c->tid);
+ if (c->mask & SD_BUS_CREDS_UID)
+ fprintf(f, " UID=%lu", (unsigned long) c->uid);
+ r = sd_bus_creds_get_owner_uid(c, &owner);
+ if (r >= 0)
+ fprintf(f, " OwnerUID=%lu", (unsigned long) owner);
+ if (c->mask & SD_BUS_CREDS_GID)
+ fprintf(f, " GID=%lu", (unsigned long) c->gid);
+
+ if ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID|SD_BUS_CREDS_UID|SD_BUS_CREDS_GID)) || r >= 0)
+ fputs("\n", f);
+
+ if (c->mask & SD_BUS_CREDS_EXE)
+ fprintf(f, " Exe=%s", c->exe);
+ if (c->mask & SD_BUS_CREDS_COMM)
+ fprintf(f, " Comm=%s", c->comm);
+ if (c->mask & SD_BUS_CREDS_TID_COMM)
+ fprintf(f, " TIDComm=%s", c->tid_comm);
+ if (c->mask & SD_BUS_CREDS_SELINUX_CONTEXT)
+ fprintf(f, " Label=%s", c->label);
+
+ if (c->mask & (SD_BUS_CREDS_EXE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_SELINUX_CONTEXT))
+ fputs("\n", f);
+
+ if (sd_bus_creds_get_cmdline(c, &cmdline) >= 0) {
+ char **i;
+
+ fputs(" CommandLine={", f);
+ STRV_FOREACH(i, cmdline) {
+ if (i != cmdline)
+ fputc(' ', f);
+
+ fputs(*i, f);
+ }
+
+ fputs("}\n", f);
+ }
+
+ if (c->mask & SD_BUS_CREDS_CGROUP)
+ fprintf(f, " CGroup=%s", c->cgroup);
+ sd_bus_creds_get_unit(c, &u);
+ if (u)
+ fprintf(f, " Unit=%s", u);
+ sd_bus_creds_get_user_unit(c, &uu);
+ if (uu)
+ fprintf(f, " UserUnit=%s", uu);
+ sd_bus_creds_get_slice(c, &sl);
+ if (sl)
+ fprintf(f, " Slice=%s", sl);
+ sd_bus_creds_get_session(c, &s);
+ if (s)
+ fprintf(f, " Session=%s", s);
+
+ if ((c->mask & SD_BUS_CREDS_CGROUP) || u || uu || sl || s)
+ fputs("\n", f);
+
+ if (sd_bus_creds_get_audit_login_uid(c, &audit_loginuid) >= 0) {
+ audit_loginuid_is_set = true;
+ fprintf(f, " AuditLoginUID=%lu", (unsigned long) audit_loginuid);
+ }
+ if (sd_bus_creds_get_audit_session_id(c, &audit_sessionid) >= 0) {
+ audit_sessionid_is_set = true;
+ fprintf(f, " AuditSessionID=%lu", (unsigned long) audit_sessionid);
+ }
+
+ if (audit_loginuid_is_set || audit_sessionid_is_set)
+ fputs("\n", f);
+
+ if (c->mask & SD_BUS_CREDS_UNIQUE_NAME)
+ fprintf(f, " UniqueName=%s", c->unique_name);
+
+ if (sd_bus_creds_get_well_known_names(c, &well_known) >= 0) {
+ char **i;
+
+ fputs(" WellKnownNames={", f);
+ STRV_FOREACH(i, well_known) {
+ if (i != well_known)
+ fputc(' ', f);
+
+ fputs(*i, f);
+ }
+
+ fputc('}', f);
+ }
+
+ if (c->mask & SD_BUS_CREDS_UNIQUE_NAME || well_known)
+ fputc('\n', f);
+
+ dump_capabilities(c, f, "EffectiveCapabilities", sd_bus_creds_has_effective_cap);
+ dump_capabilities(c, f, "PermittedCapabilities", sd_bus_creds_has_permitted_cap);
+ dump_capabilities(c, f, "InheritableCapabilities", sd_bus_creds_has_inheritable_cap);
+ dump_capabilities(c, f, "BoundingCapabilities", sd_bus_creds_has_bounding_cap);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/bus-dump.h b/src/libsystemd/sd-bus/bus-dump.h
new file mode 100644
index 0000000..bb1d25d
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-dump.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "sd-bus.h"
+
+int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header);
+
+int bus_creds_dump(sd_bus_creds *c, FILE *f);
diff --git a/src/libsystemd/sd-bus/bus-error-mapping.gperf b/src/libsystemd/sd-bus/bus-error-mapping.gperf
new file mode 100644
index 0000000..df2c4d4
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-error-mapping.gperf
@@ -0,0 +1,49 @@
+%{
+#include <errno.h>
+#include "bus-error.h"
+%}
+name_error_mapping;
+%null_strings
+%language=ANSI-C
+%define slot-name name
+%define hash-function-name bus_error_mapping_hash
+%define lookup-function-name bus_error_mapping_lookup
+%readonly-tables
+%omit-struct-type
+%struct-type
+%includes
+%%
+org.freedesktop.DBus.Error.Failed, EACCES
+org.freedesktop.DBus.Error.NoMemory, ENOMEM
+org.freedesktop.DBus.Error.ServiceUnknown, EHOSTUNREACH
+org.freedesktop.DBus.Error.NameHasNoOwner, ENXIO
+org.freedesktop.DBus.Error.NoReply, ETIMEDOUT
+org.freedesktop.DBus.Error.IOError, EIO
+org.freedesktop.DBus.Error.BadAddress, EADDRNOTAVAIL
+org.freedesktop.DBus.Error.NotSupported, ENOTSUP
+org.freedesktop.DBus.Error.LimitsExceeded, ENOBUFS
+org.freedesktop.DBus.Error.AccessDenied, EACCES
+org.freedesktop.DBus.Error.AuthFailed, EACCES
+org.freedesktop.DBus.Error.NoServer, EHOSTDOWN
+org.freedesktop.DBus.Error.Timeout, ETIMEDOUT
+org.freedesktop.DBus.Error.NoNetwork, ENONET
+org.freedesktop.DBus.Error.AddressInUse, EADDRINUSE
+org.freedesktop.DBus.Error.Disconnected, ECONNRESET
+org.freedesktop.DBus.Error.InvalidArgs, EINVAL
+org.freedesktop.DBus.Error.FileNotFound, ENOENT
+org.freedesktop.DBus.Error.FileExists, EEXIST
+org.freedesktop.DBus.Error.UnknownMethod, EBADR
+org.freedesktop.DBus.Error.UnknownObject, EBADR
+org.freedesktop.DBus.Error.UnknownInterface, EBADR
+org.freedesktop.DBus.Error.UnknownProperty, EBADR
+org.freedesktop.DBus.Error.PropertyReadOnly, EROFS
+org.freedesktop.DBus.Error.UnixProcessIdUnknown, ESRCH
+org.freedesktop.DBus.Error.InvalidSignature, EINVAL
+org.freedesktop.DBus.Error.InconsistentMessage, EBADMSG
+#
+org.freedesktop.DBus.Error.TimedOut, ETIMEDOUT
+org.freedesktop.DBus.Error.MatchRuleInvalid, EINVAL
+org.freedesktop.DBus.Error.InvalidFileContent, EINVAL
+org.freedesktop.DBus.Error.MatchRuleNotFound, ENOENT
+org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown, ESRCH
+org.freedesktop.DBus.Error.ObjectPathInUse, EBUSY
diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c
new file mode 100644
index 0000000..c2e41fb
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-error.c
@@ -0,0 +1,504 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "util.h"
+#include "errno-list.h"
+
+#include "sd-bus.h"
+#include "bus-error.h"
+
+#define BUS_ERROR_OOM SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_MEMORY, "Out of memory")
+#define BUS_ERROR_FAILED SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FAILED, "Operation failed")
+
+static int bus_error_name_to_errno(const char *name) {
+ const char *p;
+ int r;
+ const name_error_mapping *m;
+
+ if (!name)
+ return EINVAL;
+
+ p = startswith(name, "System.Error.");
+ if (p) {
+ r = errno_from_name(p);
+ if (r <= 0)
+ return EIO;
+
+ return r;
+ }
+
+ m = bus_error_mapping_lookup(name, strlen(name));
+ if (m)
+ return m->code;
+
+ return EIO;
+}
+
+static sd_bus_error errno_to_bus_error_const(int error) {
+
+ if (error < 0)
+ error = -error;
+
+ switch (error) {
+
+ case ENOMEM:
+ return BUS_ERROR_OOM;
+
+ case EPERM:
+ case EACCES:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied");
+
+ case EINVAL:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument");
+
+ case ESRCH:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process");
+
+ case ENOENT:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found");
+
+ case EEXIST:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists");
+
+ case ETIMEDOUT:
+ case ETIME:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out");
+
+ case EIO:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error");
+
+ case ENETRESET:
+ case ECONNABORTED:
+ case ECONNRESET:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected");
+
+ case ENOTSUP:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported");
+
+ case EADDRNOTAVAIL:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available");
+
+ case ENOBUFS:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded");
+
+ case EADDRINUSE:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use");
+
+ case EBADMSG:
+ return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message");
+ }
+
+ return SD_BUS_ERROR_NULL;
+}
+
+static int errno_to_bus_error_name_new(int error, char **ret) {
+ const char *name;
+ char *n;
+
+ if (error < 0)
+ error = -error;
+
+ name = errno_to_name(error);
+ if (!name)
+ return 0;
+
+ n = strappend("System.Error.", name);
+ if (!n)
+ return -ENOMEM;
+
+ *ret = n;
+ return 1;
+}
+
+bool bus_error_is_dirty(sd_bus_error *e) {
+ if (!e)
+ return false;
+
+ return e->name || e->message || e->_need_free != 0;
+}
+
+_public_ void sd_bus_error_free(sd_bus_error *e) {
+ if (!e)
+ return;
+
+ if (e->_need_free > 0) {
+ free((void*) e->name);
+ free((void*) e->message);
+ }
+
+ e->name = e->message = NULL;
+ e->_need_free = 0;
+}
+
+_public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
+
+ if (!name)
+ return 0;
+ if (!e)
+ goto finish;
+
+ assert_return(!bus_error_is_dirty(e), -EINVAL);
+
+ e->name = strdup(name);
+ if (!e->name) {
+ *e = BUS_ERROR_OOM;
+ return -ENOMEM;
+ }
+
+ if (message)
+ e->message = strdup(message);
+
+ e->_need_free = 1;
+
+finish:
+ return -bus_error_name_to_errno(name);
+}
+
+int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
+
+ if (!name)
+ return 0;
+ if (!e)
+ goto finish;
+
+ assert_return(!bus_error_is_dirty(e), -EINVAL);
+
+ e->name = strdup(name);
+ if (!e->name) {
+ *e = BUS_ERROR_OOM;
+ return -ENOMEM;
+ }
+
+ if (format)
+ vasprintf((char**) &e->message, format, ap);
+
+ e->_need_free = 1;
+
+finish:
+ return -bus_error_name_to_errno(name);
+}
+
+_public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
+
+ if (format) {
+ int r;
+ va_list ap;
+
+ va_start(ap, format);
+ r = bus_error_setfv(e, name, format, ap);
+ va_end(ap);
+
+ return r;
+ }
+
+ return sd_bus_error_set(e, name, NULL);
+}
+
+_public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
+
+ if (!sd_bus_error_is_set(e))
+ return 0;
+ if (!dest)
+ goto finish;
+
+ assert_return(!bus_error_is_dirty(dest), -EINVAL);
+
+ /*
+ * _need_free < 0 indicates that the error is temporarily const, needs deep copying
+ * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
+ * _need_free > 0 indicates that the error is fully dynamic, needs deep copying
+ */
+
+ if (e->_need_free == 0)
+ *dest = *e;
+ else {
+ dest->name = strdup(e->name);
+ if (!dest->name) {
+ *dest = BUS_ERROR_OOM;
+ return -ENOMEM;
+ }
+
+ if (e->message)
+ dest->message = strdup(e->message);
+
+ dest->_need_free = 1;
+ }
+
+finish:
+ return -bus_error_name_to_errno(e->name);
+}
+
+_public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
+ if (!name)
+ return 0;
+ if (!e)
+ goto finish;
+
+ assert_return(!bus_error_is_dirty(e), -EINVAL);
+
+ *e = SD_BUS_ERROR_MAKE_CONST(name, message);
+
+finish:
+ return -bus_error_name_to_errno(name);
+}
+
+_public_ int sd_bus_error_is_set(const sd_bus_error *e) {
+ if (!e)
+ return 0;
+
+ return !!e->name;
+}
+
+_public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
+ if (!e)
+ return 0;
+
+ return streq_ptr(e->name, name);
+}
+
+_public_ int sd_bus_error_get_errno(const sd_bus_error* e) {
+ if (!e)
+ return 0;
+
+ if (!e->name)
+ return 0;
+
+ return bus_error_name_to_errno(e->name);
+}
+
+static void bus_error_strerror(sd_bus_error *e, int error) {
+ size_t k = 64;
+ char *m;
+
+ assert(e);
+
+ for (;;) {
+ char *x;
+
+ m = new(char, k);
+ if (!m)
+ return;
+
+ errno = 0;
+ x = strerror_r(error, m, k);
+ if (errno == ERANGE || strlen(x) >= k - 1) {
+ free(m);
+ k *= 2;
+ continue;
+ }
+
+ if (!x || errno) {
+ free(m);
+ return;
+ }
+
+ if (x == m) {
+ if (e->_need_free > 0) {
+ /* Error is already dynamic, let's just update the message */
+ free((char*) e->message);
+ e->message = x;
+
+ } else {
+ char *t;
+ /* Error was const so far, let's make it dynamic, if we can */
+
+ t = strdup(e->name);
+ if (!t) {
+ free(m);
+ return;
+ }
+
+ e->_need_free = 1;
+ e->name = t;
+ e->message = x;
+ }
+ } else {
+ free(m);
+
+ if (e->_need_free > 0) {
+ char *t;
+
+ /* Error is dynamic, let's hence make the message also dynamic */
+ t = strdup(x);
+ if (!t)
+ return;
+
+ free((char*) e->message);
+ e->message = t;
+ } else {
+ /* Error is const, hence we can just override */
+ e->message = x;
+ }
+ }
+
+ return;
+ }
+}
+
+_public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
+
+ if (error < 0)
+ error = -error;
+
+ if (!e)
+ return -error;
+ if (error == 0)
+ return -error;
+
+ assert_return(!bus_error_is_dirty(e), -EINVAL);
+
+ /* First, try a const translation */
+ *e = errno_to_bus_error_const(error);
+
+ if (!sd_bus_error_is_set(e)) {
+ int k;
+
+ /* If that didn't work, try a dynamic one. */
+
+ k = errno_to_bus_error_name_new(error, (char**) &e->name);
+ if (k > 0)
+ e->_need_free = 1;
+ else if (k < 0) {
+ *e = BUS_ERROR_OOM;
+ return -error;
+ } else
+ *e = BUS_ERROR_FAILED;
+ }
+
+ /* Now, fill in the message from strerror() if we can */
+ bus_error_strerror(e, error);
+ return -error;
+}
+
+int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
+ int r;
+
+ if (error < 0)
+ error = -error;
+
+ if (!e)
+ return -error;
+ if (error == 0)
+ return 0;
+
+ assert_return(!bus_error_is_dirty(e), -EINVAL);
+
+ /* First, try a const translation */
+ *e = errno_to_bus_error_const(error);
+
+ if (!sd_bus_error_is_set(e)) {
+ int k;
+
+ /* If that didn't work, try a dynamic one */
+
+ k = errno_to_bus_error_name_new(error, (char**) &e->name);
+ if (k > 0)
+ e->_need_free = 1;
+ else if (k < 0) {
+ *e = BUS_ERROR_OOM;
+ return -ENOMEM;
+ } else
+ *e = BUS_ERROR_FAILED;
+ }
+
+ if (format) {
+ char *m;
+
+ /* First, let's try to fill in the supplied message */
+
+ r = vasprintf(&m, format, ap);
+ if (r >= 0) {
+
+ if (e->_need_free <= 0) {
+ char *t;
+
+ t = strdup(e->name);
+ if (t) {
+ e->_need_free = 1;
+ e->name = t;
+ e->message = m;
+ return -error;
+ }
+
+ free(m);
+ } else {
+ free((char*) e->message);
+ e->message = m;
+ return -error;
+ }
+ }
+ }
+
+ /* If that didn't work, use strerror() for the message */
+ bus_error_strerror(e, error);
+ return -error;
+}
+
+_public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
+ int r;
+
+ if (error < 0)
+ error = -error;
+
+ if (!e)
+ return -error;
+ if (error == 0)
+ return 0;
+
+ assert_return(!bus_error_is_dirty(e), -EINVAL);
+
+ if (format) {
+ va_list ap;
+
+ va_start(ap, format);
+ r = bus_error_set_errnofv(e, error, format, ap);
+ va_end(ap);
+
+ return r;
+ }
+
+ return sd_bus_error_set_errno(e, error);
+}
+
+const char *bus_error_message(const sd_bus_error *e, int error) {
+
+ if (e) {
+ /* Sometimes the D-Bus server is a little bit too verbose with
+ * its error messages, so let's override them here */
+ if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
+ return "Access denied";
+
+ if (e->message)
+ return e->message;
+ }
+
+ if (error < 0)
+ error = -error;
+
+ return strerror(error);
+}
diff --git a/src/libsystemd/sd-bus/bus-error.h b/src/libsystemd/sd-bus/bus-error.h
new file mode 100644
index 0000000..cf0ad9d
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-error.h
@@ -0,0 +1,42 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "sd-bus.h"
+#include "macro.h"
+
+struct name_error_mapping {
+ const char* name;
+ int code;
+};
+typedef struct name_error_mapping name_error_mapping;
+
+const name_error_mapping* bus_error_mapping_lookup(const char *str, unsigned int len);
+
+bool bus_error_is_dirty(sd_bus_error *e);
+
+const char *bus_error_message(const sd_bus_error *e, int error);
+
+int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) _printf_(3,0);
+int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _printf_(3,0);
diff --git a/src/libsystemd/sd-bus/bus-gvariant.c b/src/libsystemd/sd-bus/bus-gvariant.c
new file mode 100644
index 0000000..dc40009
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-gvariant.c
@@ -0,0 +1,249 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "bus-type.h"
+#include "bus-gvariant.h"
+#include "bus-signature.h"
+
+int bus_gvariant_get_size(const char *signature) {
+ const char *p;
+ int sum = 0, r;
+
+ /* For fixed size structs. Fails for variable size structs. */
+
+ p = signature;
+ while (*p != 0) {
+ size_t n;
+
+ r = signature_element_length(p, &n);
+ if (r < 0)
+ return r;
+ else {
+ char t[n+1];
+
+ memcpy(t, p, n);
+ t[n] = 0;
+
+ r = bus_gvariant_get_alignment(t);
+ if (r < 0)
+ return r;
+
+ sum = ALIGN_TO(sum, r);
+ }
+
+ switch (*p) {
+
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_BYTE:
+ sum += 1;
+ break;
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ sum += 2;
+ break;
+
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_UNIX_FD:
+ sum += 4;
+ break;
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ sum += 8;
+ break;
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
+ char t[n-1];
+
+ memcpy(t, p + 1, n - 2);
+ t[n - 2] = 0;
+
+ r = bus_gvariant_get_size(t);
+ if (r < 0)
+ return r;
+
+ sum += r;
+ break;
+ }
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE:
+ case SD_BUS_TYPE_ARRAY:
+ case SD_BUS_TYPE_VARIANT:
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unknown signature type");
+ }
+
+ p += n;
+ }
+
+ r = bus_gvariant_get_alignment(signature);
+ if (r < 0)
+ return r;
+
+ return ALIGN_TO(sum, r);
+}
+
+int bus_gvariant_get_alignment(const char *signature) {
+ size_t alignment = 1;
+ const char *p;
+ int r;
+
+ p = signature;
+ while (*p != 0 && alignment < 8) {
+ size_t n;
+ int a;
+
+ r = signature_element_length(p, &n);
+ if (r < 0)
+ return r;
+
+ switch (*p) {
+
+ case SD_BUS_TYPE_BYTE:
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE:
+ a = 1;
+ break;
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ a = 2;
+ break;
+
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_UNIX_FD:
+ a = 4;
+ break;
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ case SD_BUS_TYPE_VARIANT:
+ a = 8;
+ break;
+
+ case SD_BUS_TYPE_ARRAY: {
+ char t[n];
+
+ memcpy(t, p + 1, n - 1);
+ t[n - 1] = 0;
+
+ a = bus_gvariant_get_alignment(t);
+ break;
+ }
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
+ char t[n-1];
+
+ memcpy(t, p + 1, n - 2);
+ t[n - 2] = 0;
+
+ a = bus_gvariant_get_alignment(t);
+ break;
+ }
+
+ default:
+ assert_not_reached("Unknown signature type");
+ }
+
+ if (a < 0)
+ return a;
+
+ assert(a > 0 && a <= 8);
+ if ((size_t) a > alignment)
+ alignment = (size_t) a;
+
+ p += n;
+ }
+
+ return alignment;
+}
+
+int bus_gvariant_is_fixed_size(const char *signature) {
+ const char *p;
+ int r;
+
+ assert(signature);
+
+ p = signature;
+ while (*p != 0) {
+ size_t n;
+
+ r = signature_element_length(p, &n);
+ if (r < 0)
+ return r;
+
+ switch (*p) {
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE:
+ case SD_BUS_TYPE_ARRAY:
+ case SD_BUS_TYPE_VARIANT:
+ return 0;
+
+ case SD_BUS_TYPE_BYTE:
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_UNIX_FD:
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ break;
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
+ char t[n-1];
+
+ memcpy(t, p + 1, n - 2);
+ t[n - 2] = 0;
+
+ r = bus_gvariant_is_fixed_size(t);
+ if (r <= 0)
+ return r;
+ break;
+ }
+
+ default:
+ assert_not_reached("Unknown signature type");
+ }
+
+ p += n;
+ }
+
+ return true;
+}
diff --git a/src/libsystemd/sd-bus/bus-gvariant.h b/src/libsystemd/sd-bus/bus-gvariant.h
new file mode 100644
index 0000000..b4bd2a5
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-gvariant.h
@@ -0,0 +1,26 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int bus_gvariant_get_size(const char *signature) _pure_;
+int bus_gvariant_get_alignment(const char *signature) _pure_;
+int bus_gvariant_is_fixed_size(const char *signature) _pure_;
diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c
new file mode 100644
index 0000000..0bea8ca
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-internal.c
@@ -0,0 +1,305 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "bus-internal.h"
+
+bool object_path_is_valid(const char *p) {
+ const char *q;
+ bool slash;
+
+ if (!p)
+ return false;
+
+ if (p[0] != '/')
+ return false;
+
+ if (p[1] == 0)
+ return true;
+
+ for (slash = true, q = p+1; *q; q++)
+ if (*q == '/') {
+ if (slash)
+ return false;
+
+ slash = true;
+ } else {
+ bool good;
+
+ good =
+ (*q >= 'a' && *q <= 'z') ||
+ (*q >= 'A' && *q <= 'Z') ||
+ (*q >= '0' && *q <= '9') ||
+ *q == '_';
+
+ if (!good)
+ return false;
+
+ slash = false;
+ }
+
+ if (slash)
+ return false;
+
+ return true;
+}
+
+char* object_path_startswith(const char *a, const char *b) {
+ const char *p;
+
+ if (!object_path_is_valid(a) ||
+ !object_path_is_valid(b))
+ return NULL;
+
+ if (streq(b, "/"))
+ return (char*) a + 1;
+
+ p = startswith(a, b);
+ if (!p)
+ return NULL;
+
+ if (*p == 0)
+ return (char*) p;
+
+ if (*p == '/')
+ return (char*) p + 1;
+
+ return NULL;
+}
+
+bool interface_name_is_valid(const char *p) {
+ const char *q;
+ bool dot, found_dot = false;
+
+ if (isempty(p))
+ return false;
+
+ for (dot = true, q = p; *q; q++)
+ if (*q == '.') {
+ if (dot)
+ return false;
+
+ found_dot = dot = true;
+ } else {
+ bool good;
+
+ good =
+ (*q >= 'a' && *q <= 'z') ||
+ (*q >= 'A' && *q <= 'Z') ||
+ (!dot && *q >= '0' && *q <= '9') ||
+ *q == '_';
+
+ if (!good)
+ return false;
+
+ dot = false;
+ }
+
+ if (q - p > 255)
+ return false;
+
+ if (dot)
+ return false;
+
+ if (!found_dot)
+ return false;
+
+ return true;
+}
+
+bool service_name_is_valid(const char *p) {
+ const char *q;
+ bool dot, found_dot = false, unique;
+
+ if (isempty(p))
+ return false;
+
+ unique = p[0] == ':';
+
+ for (dot = true, q = unique ? p+1 : p; *q; q++)
+ if (*q == '.') {
+ if (dot)
+ return false;
+
+ found_dot = dot = true;
+ } else {
+ bool good;
+
+ good =
+ (*q >= 'a' && *q <= 'z') ||
+ (*q >= 'A' && *q <= 'Z') ||
+ ((!dot || unique) && *q >= '0' && *q <= '9') ||
+ *q == '_' || *q == '-';
+
+ if (!good)
+ return false;
+
+ dot = false;
+ }
+
+ if (q - p > 255)
+ return false;
+
+ if (dot)
+ return false;
+
+ if (!found_dot)
+ return false;
+
+ return true;
+}
+
+bool member_name_is_valid(const char *p) {
+ const char *q;
+
+ if (isempty(p))
+ return false;
+
+ for (q = p; *q; q++) {
+ bool good;
+
+ good =
+ (*q >= 'a' && *q <= 'z') ||
+ (*q >= 'A' && *q <= 'Z') ||
+ (*q >= '0' && *q <= '9') ||
+ *q == '_';
+
+ if (!good)
+ return false;
+ }
+
+ if (q - p > 255)
+ return false;
+
+ return true;
+}
+
+static bool complex_pattern_check(char c, const char *a, const char *b) {
+ bool separator = false;
+
+ if (!a && !b)
+ return true;
+
+ if (!a || !b)
+ return false;
+
+ for (;;) {
+ if (*a != *b)
+ return (separator && (*a == 0 || *b == 0)) ||
+ (*a == 0 && *b == c && b[1] == 0) ||
+ (*b == 0 && *a == c && a[1] == 0);
+
+ if (*a == 0)
+ return true;
+
+ separator = *a == c;
+
+ a++, b++;
+ }
+}
+
+bool namespace_complex_pattern(const char *pattern, const char *value) {
+ return complex_pattern_check('.', pattern, value);
+}
+
+bool path_complex_pattern(const char *pattern, const char *value) {
+ return complex_pattern_check('/', pattern, value);
+}
+
+static bool simple_pattern_check(char c, const char *a, const char *b) {
+
+ if (!a && !b)
+ return true;
+
+ if (!a || !b)
+ return false;
+
+ for (;;) {
+ if (*a != *b)
+ return *a == 0 && *b == c;
+
+ if (*a == 0)
+ return true;
+
+ a++, b++;
+ }
+}
+
+bool namespace_simple_pattern(const char *pattern, const char *value) {
+ return simple_pattern_check('.', pattern, value);
+}
+
+bool path_simple_pattern(const char *pattern, const char *value) {
+ return simple_pattern_check('/', pattern, value);
+}
+
+int bus_message_type_from_string(const char *s, uint8_t *u) {
+ if (streq(s, "signal"))
+ *u = SD_BUS_MESSAGE_SIGNAL;
+ else if (streq(s, "method_call"))
+ *u = SD_BUS_MESSAGE_METHOD_CALL;
+ else if (streq(s, "error"))
+ *u = SD_BUS_MESSAGE_METHOD_ERROR;
+ else if (streq(s, "method_return"))
+ *u = SD_BUS_MESSAGE_METHOD_RETURN;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+const char *bus_message_type_to_string(uint8_t u) {
+ if (u == SD_BUS_MESSAGE_SIGNAL)
+ return "signal";
+ else if (u == SD_BUS_MESSAGE_METHOD_CALL)
+ return "method_call";
+ else if (u == SD_BUS_MESSAGE_METHOD_ERROR)
+ return "error";
+ else if (u == SD_BUS_MESSAGE_METHOD_RETURN)
+ return "method_return";
+ else
+ return NULL;
+}
+
+char *bus_address_escape(const char *v) {
+ const char *a;
+ char *r, *b;
+
+ r = new(char, strlen(v)*3+1);
+ if (!r)
+ return NULL;
+
+ for (a = v, b = r; *a; a++) {
+
+ if ((*a >= '0' && *a <= '9') ||
+ (*a >= 'a' && *a <= 'z') ||
+ (*a >= 'A' && *a <= 'Z') ||
+ strchr("_-/.", *a))
+ *(b++) = *a;
+ else {
+ *(b++) = '%';
+ *(b++) = hexchar(*a >> 4);
+ *(b++) = hexchar(*a & 0xF);
+ }
+ }
+
+ *b = 0;
+ return r;
+}
diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h
new file mode 100644
index 0000000..7c92293
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -0,0 +1,328 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <pthread.h>
+
+#include "hashmap.h"
+#include "prioq.h"
+#include "list.h"
+#include "util.h"
+#include "refcnt.h"
+
+#include "sd-bus.h"
+#include "bus-error.h"
+#include "bus-match.h"
+#include "bus-kernel.h"
+#include "kdbus.h"
+
+struct reply_callback {
+ sd_bus_message_handler_t callback;
+ void *userdata;
+ usec_t timeout;
+ uint64_t cookie;
+ unsigned prioq_idx;
+};
+
+struct filter_callback {
+ sd_bus_message_handler_t callback;
+ void *userdata;
+
+ unsigned last_iteration;
+
+ LIST_FIELDS(struct filter_callback, callbacks);
+};
+
+struct node {
+ char *path;
+ struct node *parent;
+ LIST_HEAD(struct node, child);
+ LIST_FIELDS(struct node, siblings);
+
+ LIST_HEAD(struct node_callback, callbacks);
+ LIST_HEAD(struct node_vtable, vtables);
+ LIST_HEAD(struct node_enumerator, enumerators);
+
+ bool object_manager;
+};
+
+struct node_callback {
+ struct node *node;
+
+ bool is_fallback;
+ sd_bus_message_handler_t callback;
+ void *userdata;
+
+ unsigned last_iteration;
+
+ LIST_FIELDS(struct node_callback, callbacks);
+};
+
+struct node_enumerator {
+ struct node *node;
+
+ sd_bus_node_enumerator_t callback;
+ void *userdata;
+
+ unsigned last_iteration;
+
+ LIST_FIELDS(struct node_enumerator, enumerators);
+};
+
+struct node_vtable {
+ struct node *node;
+
+ char *interface;
+ bool is_fallback;
+ const sd_bus_vtable *vtable;
+ void *userdata;
+ sd_bus_object_find_t find;
+
+ unsigned last_iteration;
+
+ LIST_FIELDS(struct node_vtable, vtables);
+};
+
+struct vtable_member {
+ const char *path;
+ const char *interface;
+ const char *member;
+ struct node_vtable *parent;
+ unsigned last_iteration;
+ const sd_bus_vtable *vtable;
+};
+
+enum bus_state {
+ BUS_UNSET,
+ BUS_OPENING,
+ BUS_AUTHENTICATING,
+ BUS_HELLO,
+ BUS_RUNNING,
+ BUS_CLOSING,
+ BUS_CLOSED
+};
+
+static inline bool BUS_IS_OPEN(enum bus_state state) {
+ return state > BUS_UNSET && state < BUS_CLOSING;
+}
+
+enum bus_auth {
+ _BUS_AUTH_INVALID,
+ BUS_AUTH_EXTERNAL,
+ BUS_AUTH_ANONYMOUS
+};
+
+struct sd_bus {
+ /* We use atomic ref counting here since sd_bus_message
+ objects retain references to their originating sd_bus but
+ we want to allow them to be processed in a different
+ thread. We won't provide full thread safety, but only the
+ bare minimum that makes it possible to use sd_bus and
+ sd_bus_message objects independently and on different
+ threads as long as each object is used only once at the
+ same time. */
+ RefCount n_ref;
+
+ enum bus_state state;
+ int input_fd, output_fd;
+ int message_version;
+ int message_endian;
+
+ bool is_kernel:1;
+ bool can_fds:1;
+ bool bus_client:1;
+ bool ucred_valid:1;
+ bool is_server:1;
+ bool anonymous_auth:1;
+ bool prefer_readv:1;
+ bool prefer_writev:1;
+ bool match_callbacks_modified:1;
+ bool filter_callbacks_modified:1;
+ bool nodes_modified:1;
+ bool trusted:1;
+ bool fake_creds_valid:1;
+ bool manual_peer_interface:1;
+
+ int use_memfd;
+
+ void *rbuffer;
+ size_t rbuffer_size;
+
+ sd_bus_message **rqueue;
+ unsigned rqueue_size;
+ size_t rqueue_allocated;
+
+ sd_bus_message **wqueue;
+ unsigned wqueue_size;
+ size_t windex;
+ size_t wqueue_allocated;
+
+ uint64_t cookie;
+
+ char *unique_name;
+ uint64_t unique_id;
+
+ struct bus_match_node match_callbacks;
+ Prioq *reply_callbacks_prioq;
+ Hashmap *reply_callbacks;
+ LIST_HEAD(struct filter_callback, filter_callbacks);
+
+ Hashmap *nodes;
+ Hashmap *vtable_methods;
+ Hashmap *vtable_properties;
+
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+ } sockaddr;
+ socklen_t sockaddr_size;
+
+ char *kernel;
+ char *machine;
+
+ sd_id128_t server_id;
+
+ char *address;
+ unsigned address_index;
+
+ int last_connect_error;
+
+ enum bus_auth auth;
+ size_t auth_rbegin;
+ struct iovec auth_iovec[3];
+ unsigned auth_index;
+ char *auth_buffer;
+ usec_t auth_timeout;
+
+ struct ucred ucred;
+ char label[NAME_MAX];
+
+ uint64_t creds_mask;
+
+ int *fds;
+ unsigned n_fds;
+
+ char *exec_path;
+ char **exec_argv;
+
+ uint64_t hello_cookie;
+ unsigned iteration_counter;
+
+ void *kdbus_buffer;
+
+ /* We do locking around the memfd cache, since we want to
+ * allow people to process a sd_bus_message in a different
+ * thread then it was generated on and free it there. Since
+ * adding something to the memfd cache might happen when a
+ * message is released, we hence need to protect this bit with
+ * a mutex. */
+ pthread_mutex_t memfd_cache_mutex;
+ struct memfd_cache memfd_cache[MEMFD_CACHE_MAX];
+ unsigned n_memfd_cache;
+
+ pid_t original_pid;
+
+ uint64_t hello_flags;
+ uint64_t attach_flags;
+
+ uint64_t match_cookie;
+
+ sd_event_source *input_io_event_source;
+ sd_event_source *output_io_event_source;
+ sd_event_source *time_event_source;
+ sd_event_source *quit_event_source;
+ sd_event *event;
+ int event_priority;
+
+ sd_bus_message *current;
+
+ sd_bus **default_bus_ptr;
+ pid_t tid;
+
+ struct kdbus_creds fake_creds;
+ char *fake_label;
+
+ char *cgroup_root;
+};
+
+#define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
+
+#define BUS_WQUEUE_MAX 1024
+#define BUS_RQUEUE_MAX 64*1024
+
+#define BUS_MESSAGE_SIZE_MAX (64*1024*1024)
+#define BUS_AUTH_SIZE_MAX (64*1024)
+
+#define BUS_CONTAINER_DEPTH 128
+
+/* Defined by the specification as maximum size of an array in
+ * bytes */
+#define BUS_ARRAY_MAX_SIZE 67108864
+
+#define BUS_FDS_MAX 1024
+
+#define BUS_EXEC_ARGV_MAX 256
+
+bool interface_name_is_valid(const char *p) _pure_;
+bool service_name_is_valid(const char *p) _pure_;
+bool member_name_is_valid(const char *p) _pure_;
+bool object_path_is_valid(const char *p) _pure_;
+char *object_path_startswith(const char *a, const char *b) _pure_;
+
+bool namespace_complex_pattern(const char *pattern, const char *value) _pure_;
+bool path_complex_pattern(const char *pattern, const char *value) _pure_;
+
+bool namespace_simple_pattern(const char *pattern, const char *value) _pure_;
+bool path_simple_pattern(const char *pattern, const char *value) _pure_;
+
+int bus_message_type_from_string(const char *s, uint8_t *u) _pure_;
+const char *bus_message_type_to_string(uint8_t u) _pure_;
+
+#define error_name_is_valid interface_name_is_valid
+
+int bus_ensure_running(sd_bus *bus);
+int bus_start_running(sd_bus *bus);
+int bus_next_address(sd_bus *bus);
+
+int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m);
+
+int bus_rqueue_make_room(sd_bus *bus);
+
+bool bus_pid_changed(sd_bus *bus);
+
+char *bus_address_escape(const char *v);
+
+#define OBJECT_PATH_FOREACH_PREFIX(prefix, path) \
+ for (char *_slash = ({ strcpy((prefix), (path)); streq((prefix), "/") ? NULL : strrchr((prefix), '/'); }) ; \
+ _slash && !(_slash[(_slash) == (prefix)] = 0); \
+ _slash = streq((prefix), "/") ? NULL : strrchr((prefix), '/'))
+
+/* If we are invoking callbacks of a bus object, ensure unreffing the
+ * bus from the callback doesn't destroy the object we are working
+ * on */
+#define BUS_DONT_DESTROY(bus) \
+ _cleanup_bus_unref_ _unused_ sd_bus *_dont_destroy_##bus = sd_bus_ref(bus)
diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c
new file mode 100644
index 0000000..d528ab2
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-introspect.c
@@ -0,0 +1,212 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "sd-bus-protocol.h"
+#include "bus-introspect.h"
+#include "bus-signature.h"
+#include "bus-internal.h"
+#include "bus-protocol.h"
+
+int introspect_begin(struct introspect *i, bool trusted) {
+ assert(i);
+
+ zero(*i);
+ i->trusted = trusted;
+
+ i->f = open_memstream(&i->introspection, &i->size);
+ if (!i->f)
+ return -ENOMEM;
+
+ fputs(BUS_INTROSPECT_DOCTYPE
+ "<node>\n", i->f);
+
+ return 0;
+}
+
+int introspect_write_default_interfaces(struct introspect *i, bool object_manager) {
+ assert(i);
+
+ fputs(BUS_INTROSPECT_INTERFACE_PEER
+ BUS_INTROSPECT_INTERFACE_INTROSPECTABLE
+ BUS_INTROSPECT_INTERFACE_PROPERTIES, i->f);
+
+ if (object_manager)
+ fputs(BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER, i->f);
+
+ return 0;
+}
+
+int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) {
+ char *node;
+
+ assert(i);
+ assert(prefix);
+
+ while ((node = set_steal_first(s))) {
+ const char *e;
+
+ e = object_path_startswith(node, prefix);
+ if (e && e[0])
+ fprintf(i->f, " <node name=\"%s\"/>\n", e);
+
+ free(node);
+ }
+
+ return 0;
+}
+
+static void introspect_write_flags(struct introspect *i, int type, int flags) {
+ if (flags & SD_BUS_VTABLE_DEPRECATED)
+ fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f);
+
+ if (type == _SD_BUS_VTABLE_METHOD && (flags & SD_BUS_VTABLE_METHOD_NO_REPLY))
+ fputs(" <annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n", i->f);
+
+ if (type == _SD_BUS_VTABLE_PROPERTY || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) {
+ if (flags & SD_BUS_VTABLE_PROPERTY_CONST)
+ fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"const\"/>\n", i->f);
+ else if (flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)
+ fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"invalidates\"/>\n", i->f);
+ else if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
+ fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"false\"/>\n", i->f);
+ }
+
+ if (!i->trusted &&
+ (type == _SD_BUS_VTABLE_METHOD || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) &&
+ !(flags & SD_BUS_VTABLE_UNPRIVILEGED))
+ fputs(" <annotation name=\"org.freedesktop.systemd1.Privileged\" value=\"true\"/>\n", i->f);
+}
+
+static int introspect_write_arguments(struct introspect *i, const char *signature, const char *direction) {
+ int r;
+
+ for (;;) {
+ size_t l;
+
+ if (!*signature)
+ return 0;
+
+ r = signature_element_length(signature, &l);
+ if (r < 0)
+ return r;
+
+ fprintf(i->f, " <arg type=\"%.*s\"", (int) l, signature);
+
+ if (direction)
+ fprintf(i->f, " direction=\"%s\"/>\n", direction);
+ else
+ fputs("/>\n", i->f);
+
+ signature += l;
+ }
+}
+
+int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) {
+ assert(i);
+ assert(v);
+
+ for (; v->type != _SD_BUS_VTABLE_END; v++) {
+
+ /* Ignore methods, signals and properties that are
+ * marked "hidden", but do show the interface
+ * itself */
+
+ if (v->type != _SD_BUS_VTABLE_START && (v->flags & SD_BUS_VTABLE_HIDDEN))
+ continue;
+
+ switch (v->type) {
+
+ case _SD_BUS_VTABLE_START:
+ if (v->flags & SD_BUS_VTABLE_DEPRECATED)
+ fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f);
+ break;
+
+ case _SD_BUS_VTABLE_METHOD:
+ fprintf(i->f, " <method name=\"%s\">\n", v->x.method.member);
+ introspect_write_arguments(i, strempty(v->x.method.signature), "in");
+ introspect_write_arguments(i, strempty(v->x.method.result), "out");
+ introspect_write_flags(i, v->type, v->flags);
+ fputs(" </method>\n", i->f);
+ break;
+
+ case _SD_BUS_VTABLE_PROPERTY:
+ case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
+ fprintf(i->f, " <property name=\"%s\" type=\"%s\" access=\"%s\">\n",
+ v->x.property.member,
+ v->x.property.signature,
+ v->type == _SD_BUS_VTABLE_WRITABLE_PROPERTY ? "readwrite" : "read");
+ introspect_write_flags(i, v->type, v->flags);
+ fputs(" </property>\n", i->f);
+ break;
+
+ case _SD_BUS_VTABLE_SIGNAL:
+ fprintf(i->f, " <signal name=\"%s\">\n", v->x.signal.member);
+ introspect_write_arguments(i, strempty(v->x.signal.signature), NULL);
+ introspect_write_flags(i, v->type, v->flags);
+ fputs(" </signal>\n", i->f);
+ break;
+ }
+
+ }
+
+ return 0;
+}
+
+int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_bus_message **reply) {
+ sd_bus_message *q;
+ int r;
+
+ assert(i);
+ assert(m);
+ assert(reply);
+
+ fputs("</node>\n", i->f);
+ fflush(i->f);
+
+ if (ferror(i->f))
+ return -ENOMEM;
+
+ r = sd_bus_message_new_method_return(m, &q);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(q, "s", i->introspection);
+ if (r < 0) {
+ sd_bus_message_unref(q);
+ return r;
+ }
+
+ *reply = q;
+ return 0;
+}
+
+void introspect_free(struct introspect *i) {
+ assert(i);
+
+ if (i->f)
+ fclose(i->f);
+
+ if (i->introspection)
+ free(i->introspection);
+
+ zero(*i);
+}
diff --git a/src/libsystemd/sd-bus/bus-introspect.h b/src/libsystemd/sd-bus/bus-introspect.h
new file mode 100644
index 0000000..98312d1
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-introspect.h
@@ -0,0 +1,42 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "sd-bus.h"
+#include "set.h"
+
+struct introspect {
+ FILE *f;
+ char *introspection;
+ size_t size;
+ bool trusted;
+};
+
+int introspect_begin(struct introspect *i, bool trusted);
+int introspect_write_default_interfaces(struct introspect *i, bool object_manager);
+int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix);
+int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v);
+int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_bus_message **reply);
+void introspect_free(struct introspect *i);
diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c
new file mode 100644
index 0000000..f85b4d5
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-kernel.c
@@ -0,0 +1,1348 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <fcntl.h>
+#include <malloc.h>
+#include <sys/mman.h>
+
+#include "util.h"
+#include "strv.h"
+
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-kernel.h"
+#include "bus-bloom.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+
+#define UNIQUE_NAME_MAX (3+DECIMAL_STR_MAX(uint64_t))
+
+int bus_kernel_parse_unique_name(const char *s, uint64_t *id) {
+ int r;
+
+ assert(s);
+ assert(id);
+
+ if (!startswith(s, ":1."))
+ return 0;
+
+ r = safe_atou64(s + 3, id);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static void append_payload_vec(struct kdbus_item **d, const void *p, size_t sz) {
+ assert(d);
+ assert(sz > 0);
+
+ *d = ALIGN8_PTR(*d);
+
+ /* Note that p can be NULL, which encodes a region full of
+ * zeroes, which is useful to optimize certain padding
+ * conditions */
+
+ (*d)->size = offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec);
+ (*d)->type = KDBUS_ITEM_PAYLOAD_VEC;
+ (*d)->vec.address = PTR_TO_UINT64(p);
+ (*d)->vec.size = sz;
+
+ *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
+}
+
+static void append_payload_memfd(struct kdbus_item **d, int memfd, size_t sz) {
+ assert(d);
+ assert(memfd >= 0);
+ assert(sz > 0);
+
+ *d = ALIGN8_PTR(*d);
+ (*d)->size = offsetof(struct kdbus_item, memfd) + sizeof(struct kdbus_memfd);
+ (*d)->type = KDBUS_ITEM_PAYLOAD_MEMFD;
+ (*d)->memfd.fd = memfd;
+ (*d)->memfd.size = sz;
+
+ *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
+}
+
+static void append_destination(struct kdbus_item **d, const char *s, size_t length) {
+ assert(d);
+ assert(s);
+
+ *d = ALIGN8_PTR(*d);
+
+ (*d)->size = offsetof(struct kdbus_item, str) + length + 1;
+ (*d)->type = KDBUS_ITEM_DST_NAME;
+ memcpy((*d)->str, s, length + 1);
+
+ *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
+}
+
+static void* append_bloom(struct kdbus_item **d, size_t length) {
+ void *r;
+
+ assert(d);
+
+ *d = ALIGN8_PTR(*d);
+
+ (*d)->size = offsetof(struct kdbus_item, data) + length;
+ (*d)->type = KDBUS_ITEM_BLOOM;
+ r = (*d)->data;
+
+ *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
+
+ return r;
+}
+
+static void append_fds(struct kdbus_item **d, const int fds[], unsigned n_fds) {
+ assert(d);
+ assert(fds);
+ assert(n_fds > 0);
+
+ *d = ALIGN8_PTR(*d);
+ (*d)->size = offsetof(struct kdbus_item, fds) + sizeof(int) * n_fds;
+ (*d)->type = KDBUS_ITEM_FDS;
+ memcpy((*d)->fds, fds, sizeof(int) * n_fds);
+
+ *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
+}
+
+static int bus_message_setup_bloom(sd_bus_message *m, void *bloom) {
+ unsigned i;
+ int r;
+
+ assert(m);
+ assert(bloom);
+
+ memset(bloom, 0, BLOOM_SIZE);
+
+ bloom_add_pair(bloom, "message-type", bus_message_type_to_string(m->header->type));
+
+ if (m->interface)
+ bloom_add_pair(bloom, "interface", m->interface);
+ if (m->member)
+ bloom_add_pair(bloom, "member", m->member);
+ if (m->path) {
+ bloom_add_pair(bloom, "path", m->path);
+ bloom_add_pair(bloom, "path-slash-prefix", m->path);
+ bloom_add_prefixes(bloom, "path-slash-prefix", m->path, '/');
+ }
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ for (i = 0; i < 64; i++) {
+ char type;
+ const char *t;
+ char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")];
+ char *e;
+
+ r = sd_bus_message_peek_type(m, &type, NULL);
+ if (r < 0)
+ return r;
+
+ if (type != SD_BUS_TYPE_STRING &&
+ type != SD_BUS_TYPE_OBJECT_PATH &&
+ type != SD_BUS_TYPE_SIGNATURE)
+ break;
+
+ r = sd_bus_message_read_basic(m, type, &t);
+ if (r < 0)
+ return r;
+
+ e = stpcpy(buf, "arg");
+ if (i < 10)
+ *(e++) = '0' + (char) i;
+ else {
+ *(e++) = '0' + (char) (i / 10);
+ *(e++) = '0' + (char) (i % 10);
+ }
+
+ *e = 0;
+ bloom_add_pair(bloom, buf, t);
+
+ strcpy(e, "-dot-prefix");
+ bloom_add_prefixes(bloom, buf, t, '.');
+ strcpy(e, "-slash-prefix");
+ bloom_add_prefixes(bloom, buf, t, '/');
+ }
+
+ return 0;
+}
+
+static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
+ struct bus_body_part *part;
+ struct kdbus_item *d;
+ bool well_known;
+ uint64_t unique;
+ size_t sz, dl;
+ unsigned i;
+ int r;
+
+ assert(b);
+ assert(m);
+ assert(m->sealed);
+
+ if (m->kdbus)
+ return 0;
+
+ if (m->destination) {
+ r = bus_kernel_parse_unique_name(m->destination, &unique);
+ if (r < 0)
+ return r;
+
+ well_known = r == 0;
+ } else
+ well_known = false;
+
+ sz = offsetof(struct kdbus_msg, items);
+
+ assert_cc(ALIGN8(offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec)) ==
+ ALIGN8(offsetof(struct kdbus_item, memfd) + sizeof(struct kdbus_memfd)));
+
+ /* Add in fixed header, fields header and payload */
+ sz += (1 + m->n_body_parts) *
+ ALIGN8(offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec));
+
+ /* Add space for bloom filter */
+ sz += ALIGN8(offsetof(struct kdbus_item, data) + BLOOM_SIZE);
+
+ /* Add in well-known destination header */
+ if (well_known) {
+ dl = strlen(m->destination);
+ sz += ALIGN8(offsetof(struct kdbus_item, str) + dl + 1);
+ }
+
+ /* Add space for unix fds */
+ if (m->n_fds > 0)
+ sz += ALIGN8(offsetof(struct kdbus_item, fds) + sizeof(int)*m->n_fds);
+
+ m->kdbus = memalign(8, sz);
+ if (!m->kdbus) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ m->free_kdbus = true;
+ memset(m->kdbus, 0, sz);
+
+ m->kdbus->flags =
+ ((m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) |
+ ((m->header->flags & BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0);
+ m->kdbus->dst_id =
+ well_known ? 0 :
+ m->destination ? unique : KDBUS_DST_ID_BROADCAST;
+ m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS;
+ m->kdbus->cookie = m->header->serial;
+
+ if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
+ m->kdbus->cookie_reply = m->reply_cookie;
+ else
+ m->kdbus->timeout_ns = m->timeout * NSEC_PER_USEC;
+
+ d = m->kdbus->items;
+
+ if (well_known)
+ append_destination(&d, m->destination, dl);
+
+ append_payload_vec(&d, m->header, BUS_MESSAGE_BODY_BEGIN(m));
+
+ MESSAGE_FOREACH_PART(part, i, m) {
+ if (part->is_zero) {
+ /* If this is padding then simply send a
+ * vector with a NULL data pointer which the
+ * kernel will just pass through. This is the
+ * most efficient way to encode zeroes */
+
+ append_payload_vec(&d, NULL, part->size);
+ continue;
+ }
+
+ if (part->memfd >= 0 && part->sealed && m->destination) {
+ /* Try to send a memfd, if the part is
+ * sealed and this is not a broadcast. Since we can only */
+
+ append_payload_memfd(&d, part->memfd, part->size);
+ continue;
+ }
+
+ /* Otherwise let's send a vector to the actual data,
+ * for that we need to map it first. */
+ r = bus_body_part_map(part);
+ if (r < 0)
+ goto fail;
+
+ append_payload_vec(&d, part->data, part->size);
+ }
+
+ if (m->kdbus->dst_id == KDBUS_DST_ID_BROADCAST) {
+ void *p;
+
+ p = append_bloom(&d, BLOOM_SIZE);
+ r = bus_message_setup_bloom(m, p);
+ if (r < 0)
+ goto fail;
+ }
+
+ if (m->n_fds > 0)
+ append_fds(&d, m->fds, m->n_fds);
+
+ m->kdbus->size = (uint8_t*) d - (uint8_t*) m->kdbus;
+ assert(m->kdbus->size <= sz);
+
+ return 0;
+
+fail:
+ m->poisoned = true;
+ return r;
+}
+
+static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
+ sd_bus_message *m = NULL;
+ struct kdbus_item *d;
+ unsigned n_fds = 0;
+ _cleanup_free_ int *fds = NULL;
+ struct bus_header *h = NULL;
+ size_t total, n_bytes = 0, idx = 0;
+ const char *destination = NULL, *seclabel = NULL;
+ int r;
+
+ assert(bus);
+ assert(k);
+ assert(k->payload_type == KDBUS_PAYLOAD_DBUS);
+
+ KDBUS_ITEM_FOREACH(d, k, items) {
+ size_t l;
+
+ l = d->size - offsetof(struct kdbus_item, data);
+
+ switch (d->type) {
+
+ case KDBUS_ITEM_PAYLOAD_OFF:
+ if (!h) {
+ h = (struct bus_header *)((uint8_t *)k + d->vec.offset);
+
+ if (!bus_header_is_complete(h, d->vec.size))
+ return -EBADMSG;
+ }
+
+ n_bytes += d->vec.size;
+ break;
+
+ case KDBUS_ITEM_PAYLOAD_MEMFD:
+ if (!h)
+ return -EBADMSG;
+
+ n_bytes += d->memfd.size;
+ break;
+
+ case KDBUS_ITEM_FDS: {
+ int *f;
+ unsigned j;
+
+ j = l / sizeof(int);
+ f = realloc(fds, sizeof(int) * (n_fds + j));
+ if (!f)
+ return -ENOMEM;
+
+ fds = f;
+ memcpy(fds + n_fds, d->fds, sizeof(int) * j);
+ n_fds += j;
+ break;
+ }
+
+ case KDBUS_ITEM_SECLABEL:
+ seclabel = d->str;
+ break;
+ }
+ }
+
+ if (!h)
+ return -EBADMSG;
+
+ r = bus_header_message_size(h, &total);
+ if (r < 0)
+ return r;
+
+ if (n_bytes != total)
+ return -EBADMSG;
+
+ /* on kdbus we only speak native endian gvariant, never dbus1
+ * marshalling or reverse endian */
+ if (h->version != 2 ||
+ h->endian != BUS_NATIVE_ENDIAN)
+ return -EPROTOTYPE;
+
+ r = bus_message_from_header(bus, h, sizeof(struct bus_header), fds, n_fds, NULL, seclabel, 0, &m);
+ if (r < 0)
+ return r;
+
+ /* The well-known names list is different from the other
+ credentials. If we asked for it, but nothing is there, this
+ means that the list of well-known names is simply empty, not
+ that we lack any data */
+
+ m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask;
+
+ KDBUS_ITEM_FOREACH(d, k, items) {
+ size_t l;
+
+ l = d->size - offsetof(struct kdbus_item, data);
+
+ switch (d->type) {
+
+ case KDBUS_ITEM_PAYLOAD_OFF: {
+ size_t begin_body;
+
+ begin_body = BUS_MESSAGE_BODY_BEGIN(m);
+
+ if (idx + d->vec.size > begin_body) {
+ struct bus_body_part *part;
+
+ /* Contains body material */
+
+ part = message_append_part(m);
+ if (!part) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ /* A -1 offset is NUL padding. */
+ part->is_zero = d->vec.offset == ~0ULL;
+
+ if (idx >= begin_body) {
+ if (!part->is_zero)
+ part->data = (uint8_t *)k + d->vec.offset;
+ part->size = d->vec.size;
+ } else {
+ if (!part->is_zero)
+ part->data = (uint8_t *)k + d->vec.offset + (begin_body - idx);
+ part->size = d->vec.size - (begin_body - idx);
+ }
+
+ part->sealed = true;
+ }
+
+ idx += d->vec.size;
+ break;
+ }
+
+ case KDBUS_ITEM_PAYLOAD_MEMFD: {
+ struct bus_body_part *part;
+
+ if (idx < BUS_MESSAGE_BODY_BEGIN(m)) {
+ r = -EBADMSG;
+ goto fail;
+ }
+
+ part = message_append_part(m);
+ if (!part) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ part->memfd = d->memfd.fd;
+ part->size = d->memfd.size;
+ part->sealed = true;
+
+ idx += d->memfd.size;
+ break;
+ }
+
+ case KDBUS_ITEM_CREDS:
+ /* UID/GID/PID are always valid */
+ m->creds.uid = (uid_t) d->creds.uid;
+ m->creds.gid = (gid_t) d->creds.gid;
+ m->creds.pid = (pid_t) d->creds.pid;
+ m->creds.mask |= (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID) & bus->creds_mask;
+
+ /* The PID starttime/TID might be missing
+ * however, when the data is faked by some
+ * data bus proxy and it lacks that
+ * information about the real client since
+ * SO_PEERCRED is used for that */
+
+ if (d->creds.starttime > 0) {
+ m->creds.pid_starttime = d->creds.starttime / NSEC_PER_USEC;
+ m->creds.mask |= SD_BUS_CREDS_PID_STARTTIME & bus->creds_mask;
+ }
+
+ if (d->creds.tid > 0) {
+ m->creds.tid = (pid_t) d->creds.tid;
+ m->creds.mask |= SD_BUS_CREDS_TID & bus->creds_mask;
+ }
+ break;
+
+ case KDBUS_ITEM_TIMESTAMP:
+ m->realtime = d->timestamp.realtime_ns / NSEC_PER_USEC;
+ m->monotonic = d->timestamp.monotonic_ns / NSEC_PER_USEC;
+ break;
+
+ case KDBUS_ITEM_PID_COMM:
+ m->creds.comm = d->str;
+ m->creds.mask |= SD_BUS_CREDS_COMM & bus->creds_mask;
+ break;
+
+ case KDBUS_ITEM_TID_COMM:
+ m->creds.tid_comm = d->str;
+ m->creds.mask |= SD_BUS_CREDS_TID_COMM & bus->creds_mask;
+ break;
+
+ case KDBUS_ITEM_EXE:
+ m->creds.exe = d->str;
+ m->creds.mask |= SD_BUS_CREDS_EXE & bus->creds_mask;
+ break;
+
+ case KDBUS_ITEM_CMDLINE:
+ m->creds.cmdline = d->str;
+ m->creds.cmdline_size = l;
+ m->creds.mask |= SD_BUS_CREDS_CMDLINE & bus->creds_mask;
+ break;
+
+ case KDBUS_ITEM_CGROUP:
+ m->creds.cgroup = d->str;
+ m->creds.mask |= (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID) & bus->creds_mask;
+
+ if (!bus->cgroup_root) {
+ r = cg_get_root_path(&bus->cgroup_root);
+ if (r < 0)
+ goto fail;
+ }
+
+ m->creds.cgroup_root = bus->cgroup_root;
+
+ break;
+
+ case KDBUS_ITEM_AUDIT:
+ m->creds.audit_session_id = (uint32_t) d->audit.sessionid;
+ m->creds.audit_login_uid = (uid_t) d->audit.loginuid;
+ m->creds.mask |= (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID) & bus->creds_mask;
+ break;
+
+ case KDBUS_ITEM_CAPS:
+ m->creds.capability = d->data;
+ m->creds.capability_size = l;
+ m->creds.mask |= (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS) & bus->creds_mask;
+ break;
+
+ case KDBUS_ITEM_DST_NAME:
+ if (!service_name_is_valid(d->str))
+ return -EBADMSG;
+
+ destination = d->str;
+ break;
+
+ case KDBUS_ITEM_NAME:
+ if (!service_name_is_valid(d->name.name))
+ return -EBADMSG;
+
+ r = strv_extend(&m->creds.well_known_names, d->name.name);
+ if (r < 0)
+ goto fail;
+ break;
+
+ case KDBUS_ITEM_FDS:
+ case KDBUS_ITEM_SECLABEL:
+ break;
+
+ default:
+ log_debug("Got unknown field from kernel %llu", d->type);
+ }
+ }
+
+ r = bus_message_parse_fields(m);
+ if (r < 0)
+ goto fail;
+
+ /* Override information from the user header with data from the kernel */
+ if (k->src_id == KDBUS_SRC_ID_KERNEL)
+ m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus";
+ else {
+ snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id);
+ m->sender = m->creds.unique_name = m->sender_buffer;
+ }
+
+ if (destination)
+ m->destination = destination;
+ else if (k->dst_id == KDBUS_DST_ID_BROADCAST)
+ m->destination = NULL;
+ else if (k->dst_id == KDBUS_DST_ID_NAME)
+ m->destination = bus->unique_name; /* fill in unique name if the well-known name is missing */
+ else {
+ snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id);
+ m->destination = m->destination_buffer;
+ }
+
+ /* We take possession of the kmsg struct now */
+ m->kdbus = k;
+ m->release_kdbus = true;
+ m->free_fds = true;
+ fds = NULL;
+
+ bus->rqueue[bus->rqueue_size++] = m;
+
+ return 1;
+
+fail:
+ if (m) {
+ struct bus_body_part *part;
+ unsigned i;
+
+ /* Make sure the memfds are not freed twice */
+ MESSAGE_FOREACH_PART(part, i, m)
+ if (part->memfd >= 0)
+ part->memfd = -1;
+
+ sd_bus_message_unref(m);
+ }
+
+ return r;
+}
+
+int bus_kernel_take_fd(sd_bus *b) {
+ struct kdbus_cmd_hello *hello;
+ struct kdbus_item *item;
+ size_t l = 0, sz;
+ int r;
+
+ assert(b);
+
+ if (b->is_server)
+ return -EINVAL;
+
+ b->use_memfd = 1;
+
+ sz = ALIGN8(offsetof(struct kdbus_cmd_hello, items));
+
+ if (b->fake_creds_valid)
+ sz += ALIGN8(offsetof(struct kdbus_item, creds)) + sizeof(struct kdbus_creds);
+
+ if (b->fake_label) {
+ l = strlen(b->fake_label);
+ sz += ALIGN8(offsetof(struct kdbus_item, str) + l + 1);
+ }
+
+ hello = alloca0(sz);
+ hello->size = sz;
+ hello->conn_flags = b->hello_flags;
+ hello->attach_flags = b->attach_flags;
+ hello->pool_size = KDBUS_POOL_SIZE;
+
+ item = hello->items;
+
+ if (b->fake_creds_valid) {
+ item->size = offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds);
+ item->type = KDBUS_ITEM_CREDS;
+ item->creds = b->fake_creds;
+
+ item = KDBUS_ITEM_NEXT(item);
+ }
+
+ if (b->fake_label) {
+ item->size = offsetof(struct kdbus_item, str) + l + 1;
+ memcpy(item->str, b->fake_label, l+1);
+ }
+
+ r = ioctl(b->input_fd, KDBUS_CMD_HELLO, hello);
+ if (r < 0)
+ return -errno;
+
+ if (!b->kdbus_buffer) {
+ b->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, b->input_fd, 0);
+ if (b->kdbus_buffer == MAP_FAILED) {
+ b->kdbus_buffer = NULL;
+ return -errno;
+ }
+ }
+
+ /* The higher 32bit of both flags fields are considered
+ * 'incompatible flags'. Refuse them all for now. */
+ if (hello->bus_flags > 0xFFFFFFFFULL ||
+ hello->conn_flags > 0xFFFFFFFFULL)
+ return -ENOTSUP;
+
+ if (hello->bloom_size != BLOOM_SIZE)
+ return -ENOTSUP;
+
+ if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello->id) < 0)
+ return -ENOMEM;
+
+ b->unique_id = hello->id;
+
+ b->is_kernel = true;
+ b->bus_client = true;
+ b->can_fds = !!(hello->conn_flags & KDBUS_HELLO_ACCEPT_FD);
+ b->message_version = 2;
+ b->message_endian = BUS_NATIVE_ENDIAN;
+
+ /* the kernel told us the UUID of the underlying bus */
+ memcpy(b->server_id.bytes, hello->id128, sizeof(b->server_id.bytes));
+
+ return bus_start_running(b);
+}
+
+int bus_kernel_connect(sd_bus *b) {
+ assert(b);
+ assert(b->input_fd < 0);
+ assert(b->output_fd < 0);
+ assert(b->kernel);
+
+ if (b->is_server)
+ return -EINVAL;
+
+ b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (b->input_fd < 0)
+ return -errno;
+
+ b->output_fd = b->input_fd;
+
+ return bus_kernel_take_fd(b);
+}
+
+int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(bus->state == BUS_RUNNING);
+
+ /* If we can't deliver, we want room for the error message */
+ r = bus_rqueue_make_room(bus);
+ if (r < 0)
+ return r;
+
+ r = bus_message_setup_kmsg(bus, m);
+ if (r < 0)
+ return r;
+
+ r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus);
+ if (r < 0) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus_message *reply;
+
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+ else if (errno == ENXIO || errno == ESRCH) {
+
+ /* ENXIO: unique name not known
+ * ESRCH: well-known name not known */
+
+ if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
+ sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Destination %s not known", m->destination);
+ else {
+ log_debug("Could not deliver message to %s as destination is not known. Ignoring.", m->destination);
+ return 0;
+ }
+
+ } else if (errno == EADDRNOTAVAIL) {
+
+ /* EADDRNOTAVAIL: activation is possible, but turned off in request flags */
+
+ if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
+ sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Activation of %s not requested", m->destination);
+ else {
+ log_debug("Could not deliver message to %s as destination is not activated. Ignoring.", m->destination);
+ return 0;
+ }
+ } else
+ return -errno;
+
+ r = bus_message_new_synthetic_error(
+ bus,
+ BUS_MESSAGE_COOKIE(m),
+ &error,
+ &reply);
+
+ if (r < 0)
+ return r;
+
+ r = bus_seal_synthetic_message(bus, reply);
+ if (r < 0)
+ return r;
+
+ bus->rqueue[bus->rqueue_size++] = reply;
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static void close_kdbus_msg(sd_bus *bus, struct kdbus_msg *k) {
+ uint64_t off;
+ struct kdbus_item *d;
+
+ assert(bus);
+ assert(k);
+
+ off = (uint8_t *)k - (uint8_t *)bus->kdbus_buffer;
+ ioctl(bus->input_fd, KDBUS_CMD_FREE, &off);
+
+ KDBUS_ITEM_FOREACH(d, k, items) {
+
+ if (d->type == KDBUS_ITEM_FDS)
+ close_many(d->fds, (d->size - offsetof(struct kdbus_item, fds)) / sizeof(int));
+ else if (d->type == KDBUS_ITEM_PAYLOAD_MEMFD)
+ close_nointr_nofail(d->memfd.fd);
+ }
+}
+
+static int push_name_owner_changed(sd_bus *bus, const char *name, const char *old_owner, const char *new_owner) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ assert(bus);
+
+ r = sd_bus_message_new_signal(
+ bus,
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "NameOwnerChanged",
+ &m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "sss", name, old_owner, new_owner);
+ if (r < 0)
+ return r;
+
+ m->sender = "org.freedesktop.DBus";
+
+ r = bus_seal_synthetic_message(bus, m);
+ if (r < 0)
+ return r;
+
+ bus->rqueue[bus->rqueue_size++] = m;
+ m = NULL;
+
+ return 1;
+}
+
+static int translate_name_change(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) {
+ char new_owner[UNIQUE_NAME_MAX], old_owner[UNIQUE_NAME_MAX];
+
+ assert(bus);
+ assert(k);
+ assert(d);
+
+ if (d->type == KDBUS_ITEM_NAME_ADD || (d->name_change.old.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR)))
+ old_owner[0] = 0;
+ else
+ sprintf(old_owner, ":1.%llu", (unsigned long long) d->name_change.old.id);
+
+ if (d->type == KDBUS_ITEM_NAME_REMOVE || (d->name_change.new.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))) {
+
+ if (isempty(old_owner))
+ return 0;
+
+ new_owner[0] = 0;
+ } else
+ sprintf(new_owner, ":1.%llu", (unsigned long long) d->name_change.new.id);
+
+ return push_name_owner_changed(bus, d->name_change.name, old_owner, new_owner);
+}
+
+static int translate_id_change(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) {
+ char owner[UNIQUE_NAME_MAX];
+
+ assert(bus);
+ assert(k);
+ assert(d);
+
+ sprintf(owner, ":1.%llu", d->id_change.id);
+
+ return push_name_owner_changed(
+ bus, owner,
+ d->type == KDBUS_ITEM_ID_ADD ? NULL : owner,
+ d->type == KDBUS_ITEM_ID_ADD ? owner : NULL);
+}
+
+static int translate_reply(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ assert(bus);
+ assert(k);
+ assert(d);
+
+ r = bus_message_new_synthetic_error(
+ bus,
+ k->cookie_reply,
+ d->type == KDBUS_ITEM_REPLY_TIMEOUT ?
+ &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call timed out") :
+ &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call peer died"),
+ &m);
+ if (r < 0)
+ return r;
+
+ m->sender = "org.freedesktop.DBus";
+
+ r = bus_seal_synthetic_message(bus, m);
+ if (r < 0)
+ return r;
+
+ bus->rqueue[bus->rqueue_size++] = m;
+ m = NULL;
+
+ return 1;
+}
+
+static int bus_kernel_translate_message(sd_bus *bus, struct kdbus_msg *k) {
+ struct kdbus_item *d, *found = NULL;
+
+ static int (* const translate[])(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d) = {
+ [KDBUS_ITEM_NAME_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change,
+ [KDBUS_ITEM_NAME_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change,
+ [KDBUS_ITEM_NAME_CHANGE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change,
+
+ [KDBUS_ITEM_ID_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change,
+ [KDBUS_ITEM_ID_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change,
+
+ [KDBUS_ITEM_REPLY_TIMEOUT - _KDBUS_ITEM_KERNEL_BASE] = translate_reply,
+ [KDBUS_ITEM_REPLY_DEAD - _KDBUS_ITEM_KERNEL_BASE] = translate_reply,
+ };
+
+ assert(bus);
+ assert(k);
+ assert(k->payload_type == KDBUS_PAYLOAD_KERNEL);
+
+ KDBUS_ITEM_FOREACH(d, k, items) {
+ if (d->type >= _KDBUS_ITEM_KERNEL_BASE && d->type < _KDBUS_ITEM_KERNEL_BASE + ELEMENTSOF(translate)) {
+ if (found)
+ return -EBADMSG;
+ found = d;
+ } else
+ log_debug("Got unknown field from kernel %llu", d->type);
+ }
+
+ if (!found) {
+ log_debug("Didn't find a kernel message to translate.");
+ return 0;
+ }
+
+ return translate[found->type - _KDBUS_ITEM_KERNEL_BASE](bus, k, found);
+}
+
+int bus_kernel_read_message(sd_bus *bus) {
+ struct kdbus_cmd_recv recv = {};
+ struct kdbus_msg *k;
+ int r;
+
+ assert(bus);
+
+ r = bus_rqueue_make_room(bus);
+ if (r < 0)
+ return r;
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, &recv);
+ if (r < 0) {
+ if (errno == EAGAIN)
+ return 0;
+
+ return -errno;
+ }
+ k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + recv.offset);
+
+ if (k->payload_type == KDBUS_PAYLOAD_DBUS) {
+ r = bus_kernel_make_message(bus, k);
+
+ /* Anybody can send us invalid messages, let's just drop them. */
+ if (r == -EBADMSG || r == -EPROTOTYPE) {
+ log_debug("Ignoring invalid message: %s", strerror(-r));
+ r = 0;
+ }
+
+ } else if (k->payload_type == KDBUS_PAYLOAD_KERNEL)
+ r = bus_kernel_translate_message(bus, k);
+ else
+ r = 0;
+
+ if (r <= 0)
+ close_kdbus_msg(bus, k);
+
+ return r < 0 ? r : 1;
+}
+
+int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *allocated) {
+ struct memfd_cache *c;
+ int fd;
+
+ assert(address);
+ assert(mapped);
+ assert(allocated);
+
+ if (!bus || !bus->is_kernel)
+ return -ENOTSUP;
+
+ assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) >= 0);
+
+ if (bus->n_memfd_cache <= 0) {
+ struct kdbus_cmd_memfd_make cmd = {
+ .size = sizeof(struct kdbus_cmd_memfd_make),
+ };
+ int r;
+
+ assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
+
+ r = ioctl(bus->input_fd, KDBUS_CMD_MEMFD_NEW, &cmd);
+ if (r < 0)
+ return -errno;
+
+ *address = NULL;
+ *mapped = 0;
+ *allocated = 0;
+ return cmd.fd;
+ }
+
+ c = &bus->memfd_cache[--bus->n_memfd_cache];
+
+ assert(c->fd >= 0);
+ assert(c->mapped == 0 || c->address);
+
+ *address = c->address;
+ *mapped = c->mapped;
+ *allocated = c->allocated;
+ fd = c->fd;
+
+ assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
+
+ return fd;
+}
+
+static void close_and_munmap(int fd, void *address, size_t size) {
+ if (size > 0)
+ assert_se(munmap(address, PAGE_ALIGN(size)) >= 0);
+
+ close_nointr_nofail(fd);
+}
+
+void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t mapped, size_t allocated) {
+ struct memfd_cache *c;
+ uint64_t max_mapped = PAGE_ALIGN(MEMFD_CACHE_ITEM_SIZE_MAX);
+
+ assert(fd >= 0);
+ assert(mapped == 0 || address);
+
+ if (!bus || !bus->is_kernel) {
+ close_and_munmap(fd, address, mapped);
+ return;
+ }
+
+ assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) >= 0);
+
+ if (bus->n_memfd_cache >= ELEMENTSOF(bus->memfd_cache)) {
+ assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
+
+ close_and_munmap(fd, address, mapped);
+ return;
+ }
+
+ c = &bus->memfd_cache[bus->n_memfd_cache++];
+ c->fd = fd;
+ c->address = address;
+
+ /* If overly long, let's return a bit to the OS */
+ if (mapped > max_mapped) {
+ assert_se(ioctl(fd, KDBUS_CMD_MEMFD_SIZE_SET, &max_mapped) >= 0);
+ assert_se(munmap((uint8_t*) address + max_mapped, PAGE_ALIGN(mapped - max_mapped)) >= 0);
+ c->mapped = c->allocated = max_mapped;
+ } else {
+ c->mapped = mapped;
+ c->allocated = allocated;
+ }
+
+ assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
+}
+
+void bus_kernel_flush_memfd(sd_bus *b) {
+ unsigned i;
+
+ assert(b);
+
+ for (i = 0; i < b->n_memfd_cache; i++)
+ close_and_munmap(b->memfd_cache[i].fd, b->memfd_cache[i].address, b->memfd_cache[i].mapped);
+}
+
+int kdbus_translate_request_name_flags(uint64_t flags, uint64_t *kdbus_flags) {
+ uint64_t f = 0;
+
+ assert(kdbus_flags);
+
+ if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT)
+ f |= KDBUS_NAME_ALLOW_REPLACEMENT;
+
+ if (flags & SD_BUS_NAME_REPLACE_EXISTING)
+ f |= KDBUS_NAME_REPLACE_EXISTING;
+
+ if (flags & SD_BUS_NAME_QUEUE)
+ f |= KDBUS_NAME_QUEUE;
+
+ *kdbus_flags = f;
+ return 0;
+}
+
+int kdbus_translate_attach_flags(uint64_t mask, uint64_t *kdbus_mask) {
+ uint64_t m = 0;
+
+ assert(kdbus_mask);
+
+ if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID))
+ m |= KDBUS_ATTACH_CREDS;
+
+ if (mask & (SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM))
+ m |= KDBUS_ATTACH_COMM;
+
+ if (mask & SD_BUS_CREDS_EXE)
+ m |= KDBUS_ATTACH_EXE;
+
+ if (mask & SD_BUS_CREDS_CMDLINE)
+ m |= KDBUS_ATTACH_CMDLINE;
+
+ if (mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID))
+ m |= KDBUS_ATTACH_CGROUP;
+
+ if (mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS))
+ m |= KDBUS_ATTACH_CAPS;
+
+ if (mask & SD_BUS_CREDS_SELINUX_CONTEXT)
+ m |= KDBUS_ATTACH_SECLABEL;
+
+ if (mask & (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))
+ m |= KDBUS_ATTACH_AUDIT;
+
+ if (mask & SD_BUS_CREDS_WELL_KNOWN_NAMES)
+ m |= KDBUS_ATTACH_NAMES;
+
+ *kdbus_mask = m;
+ return 0;
+}
+
+int bus_kernel_create_bus(const char *name, bool world, char **s) {
+ struct kdbus_cmd_make *make;
+ struct kdbus_item *n;
+ int fd;
+
+ assert(name);
+ assert(s);
+
+ fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ make = alloca0(ALIGN8(offsetof(struct kdbus_cmd_make, items) +
+ offsetof(struct kdbus_item, data64) + sizeof(uint64_t) +
+ offsetof(struct kdbus_item, str) +
+ DECIMAL_STR_MAX(uid_t) + 1 + strlen(name) + 1));
+
+ make->size = offsetof(struct kdbus_cmd_make, items);
+
+ n = make->items;
+ n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t);
+ n->type = KDBUS_ITEM_BLOOM_SIZE;
+ n->data64[0] = BLOOM_SIZE;
+ assert_cc(BLOOM_SIZE % 8 == 0);
+ make->size += ALIGN8(n->size);
+
+ n = KDBUS_ITEM_NEXT(n);
+ sprintf(n->str, "%lu-%s", (unsigned long) getuid(), name);
+ n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1;
+ n->type = KDBUS_ITEM_MAKE_NAME;
+ make->size += ALIGN8(n->size);
+
+ make->flags = KDBUS_MAKE_POLICY_OPEN | (world ? KDBUS_MAKE_ACCESS_WORLD : 0);
+
+ if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ /* The higher 32bit of the flags field are considered
+ * 'incompatible flags'. Refuse them all for now. */
+ if (make->flags > 0xFFFFFFFFULL) {
+ close_nointr_nofail(fd);
+ return -ENOTSUP;
+ }
+
+ if (s) {
+ char *p;
+
+ p = strjoin("/dev/kdbus/", n->str, "/bus", NULL);
+ if (!p) {
+ close_nointr_nofail(fd);
+ return -ENOMEM;
+ }
+
+ *s = p;
+ }
+
+ return fd;
+}
+
+int bus_kernel_create_starter(const char *bus, const char *name) {
+ struct kdbus_cmd_hello *hello;
+ struct kdbus_item *n;
+ char *p;
+ int fd;
+
+ assert(bus);
+ assert(name);
+
+ p = alloca(sizeof("/dev/kdbus/") - 1 + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + sizeof("/bus"));
+ sprintf(p, "/dev/kdbus/%lu-%s/bus", (unsigned long) getuid(), bus);
+
+ fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ hello = alloca0(ALIGN8(offsetof(struct kdbus_cmd_hello, items) +
+ offsetof(struct kdbus_item, str) +
+ strlen(name) + 1));
+
+ n = hello->items;
+ strcpy(n->str, name);
+ n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1;
+ n->type = KDBUS_ITEM_NAME;
+
+ hello->size = ALIGN8(offsetof(struct kdbus_cmd_hello, items) + n->size);
+ hello->conn_flags = KDBUS_HELLO_ACTIVATOR;
+ hello->pool_size = KDBUS_POOL_SIZE;
+
+ if (ioctl(fd, KDBUS_CMD_HELLO, hello) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ /* The higher 32bit of both flags fields are considered
+ * 'incompatible flags'. Refuse them all for now. */
+ if (hello->bus_flags > 0xFFFFFFFFULL ||
+ hello->conn_flags > 0xFFFFFFFFULL) {
+ close_nointr_nofail(fd);
+ return -ENOTSUP;
+ }
+
+ if (hello->bloom_size != BLOOM_SIZE) {
+ close_nointr_nofail(fd);
+ return -ENOTSUP;
+ }
+
+ return fd;
+}
+
+int bus_kernel_create_namespace(const char *name, char **s) {
+ struct kdbus_cmd_make *make;
+ struct kdbus_item *n;
+ int fd;
+
+ assert(name);
+ assert(s);
+
+ fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ make = alloca0(ALIGN8(offsetof(struct kdbus_cmd_make, items) +
+ offsetof(struct kdbus_item, str) +
+ strlen(name) + 1));
+
+ n = make->items;
+ strcpy(n->str, name);
+ n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1;
+ n->type = KDBUS_ITEM_MAKE_NAME;
+
+ make->size = ALIGN8(offsetof(struct kdbus_cmd_make, items) + n->size);
+ make->flags = KDBUS_MAKE_POLICY_OPEN | KDBUS_MAKE_ACCESS_WORLD;
+
+ if (ioctl(fd, KDBUS_CMD_NS_MAKE, make) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ /* The higher 32bit of the flags field are considered
+ * 'incompatible flags'. Refuse them all for now. */
+ if (make->flags > 0xFFFFFFFFULL) {
+ close_nointr_nofail(fd);
+ return -ENOTSUP;
+ }
+
+ if (s) {
+ char *p;
+
+ p = strappend("/dev/kdbus/ns/", name);
+ if (!p) {
+ close_nointr_nofail(fd);
+ return -ENOMEM;
+ }
+
+ *s = p;
+ }
+
+ return fd;
+}
+
+int bus_kernel_create_monitor(const char *bus) {
+ struct kdbus_cmd_hello *hello;
+ char *p;
+ int fd;
+
+ assert(bus);
+
+ p = alloca(sizeof("/dev/kdbus/") - 1 + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + sizeof("/bus"));
+ sprintf(p, "/dev/kdbus/%lu-%s/bus", (unsigned long) getuid(), bus);
+
+ fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ hello = alloca0(sizeof(struct kdbus_cmd_hello));
+ hello->size = sizeof(struct kdbus_cmd_hello);
+ hello->conn_flags = KDBUS_HELLO_ACTIVATOR;
+ hello->pool_size = KDBUS_POOL_SIZE;
+
+ if (ioctl(fd, KDBUS_CMD_HELLO, hello) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ /* The higher 32bit of both flags fields are considered
+ * 'incompatible flags'. Refuse them all for now. */
+ if (hello->bus_flags > 0xFFFFFFFFULL ||
+ hello->conn_flags > 0xFFFFFFFFULL) {
+ close_nointr_nofail(fd);
+ return -ENOTSUP;
+ }
+
+ return fd;
+}
+
+int bus_kernel_try_close(sd_bus *bus) {
+ assert(bus);
+ assert(bus->is_kernel);
+
+ if (ioctl(bus->input_fd, KDBUS_CMD_BYEBYE) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/bus-kernel.h b/src/libsystemd/sd-bus/bus-kernel.h
new file mode 100644
index 0000000..2aba0bb
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-kernel.h
@@ -0,0 +1,81 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "sd-bus.h"
+
+#define KDBUS_ITEM_NEXT(item) \
+ (typeof(item))(((uint8_t *)item) + ALIGN8((item)->size))
+
+#define KDBUS_ITEM_FOREACH(part, head, first) \
+ for (part = (head)->first; \
+ (uint8_t *)(part) < (uint8_t *)(head) + (head)->size; \
+ part = KDBUS_ITEM_NEXT(part))
+
+#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data)
+#define KDBUS_ITEM_SIZE(s) ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE)
+
+#define MEMFD_CACHE_MAX 32
+
+/* When we cache a memfd block for reuse, we will truncate blocks
+ * longer than this in order not to keep too much data around. */
+#define MEMFD_CACHE_ITEM_SIZE_MAX (128*1024)
+
+/* This determines at which minimum size we prefer sending memfds over
+ * sending vectors */
+#define MEMFD_MIN_SIZE (128*1024)
+
+/* The size of the per-connection memory pool that we set up and where
+ * the kernel places our incoming messages */
+#define KDBUS_POOL_SIZE (16*1024*1024)
+
+struct memfd_cache {
+ int fd;
+ void *address;
+ size_t mapped;
+ size_t allocated;
+};
+
+int bus_kernel_connect(sd_bus *b);
+int bus_kernel_take_fd(sd_bus *b);
+
+int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m);
+int bus_kernel_read_message(sd_bus *bus);
+
+int bus_kernel_create_bus(const char *name, bool world, char **s);
+int bus_kernel_create_namespace(const char *name, char **s);
+int bus_kernel_create_starter(const char *bus, const char *name);
+int bus_kernel_create_monitor(const char *bus);
+
+int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *allocated);
+void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t mapped, size_t allocated);
+
+void bus_kernel_flush_memfd(sd_bus *bus);
+
+int bus_kernel_parse_unique_name(const char *s, uint64_t *id);
+
+int kdbus_translate_request_name_flags(uint64_t sd_bus_flags, uint64_t *kdbus_flags);
+int kdbus_translate_attach_flags(uint64_t sd_bus_flags, uint64_t *kdbus_flags);
+
+int bus_kernel_try_close(sd_bus *bus);
diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c
new file mode 100644
index 0000000..ffc9756
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-match.c
@@ -0,0 +1,1078 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-match.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "strv.h"
+
+/* Example:
+ *
+ * A: type=signal,sender=foo,interface=bar
+ * B: type=signal,sender=quux,interface=fips
+ * C: type=signal,sender=quux,interface=waldo
+ * D: type=signal,member=test
+ * E: sender=miau
+ * F: type=signal
+ * G: type=signal
+ *
+ * results in this tree:
+ *
+ * BUS_MATCH_ROOT
+ * + BUS_MATCH_MESSAGE_TYPE
+ * | ` BUS_MATCH_VALUE: value == signal
+ * | + DBUS_MATCH_SENDER
+ * | | + BUS_MATCH_VALUE: value == foo
+ * | | | ` DBUS_MATCH_INTERFACE
+ * | | | ` BUS_MATCH_VALUE: value == bar
+ * | | | ` BUS_MATCH_LEAF: A
+ * | | ` BUS_MATCH_VALUE: value == quux
+ * | | ` DBUS_MATCH_INTERFACE
+ * | | | BUS_MATCH_VALUE: value == fips
+ * | | | ` BUS_MATCH_LEAF: B
+ * | | ` BUS_MATCH_VALUE: value == waldo
+ * | | ` BUS_MATCH_LEAF: C
+ * | + DBUS_MATCH_MEMBER
+ * | | ` BUS_MATCH_VALUE: value == test
+ * | | ` BUS_MATCH_LEAF: D
+ * | + BUS_MATCH_LEAF: F
+ * | ` BUS_MATCH_LEAF: G
+ * ` BUS_MATCH_SENDER
+ * ` BUS_MATCH_VALUE: value == miau
+ * ` BUS_MATCH_LEAF: E
+ */
+
+static inline bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) {
+ return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_NAMESPACE_LAST;
+}
+
+static inline bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) {
+ return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) ||
+ (t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST);
+}
+
+static void bus_match_node_free(struct bus_match_node *node) {
+ assert(node);
+ assert(node->parent);
+ assert(!node->child);
+ assert(node->type != BUS_MATCH_ROOT);
+ assert(node->type < _BUS_MATCH_NODE_TYPE_MAX);
+
+ if (node->parent->child) {
+ /* We are apparently linked into the parent's child
+ * list. Let's remove us from there. */
+ if (node->prev) {
+ assert(node->prev->next == node);
+ node->prev->next = node->next;
+ } else {
+ assert(node->parent->child == node);
+ node->parent->child = node->next;
+ }
+
+ if (node->next)
+ node->next->prev = node->prev;
+ }
+
+ if (node->type == BUS_MATCH_VALUE) {
+ /* We might be in the parent's hash table, so clean
+ * this up */
+
+ if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
+ hashmap_remove(node->parent->compare.children, UINT_TO_PTR(node->value.u8));
+ else if (BUS_MATCH_CAN_HASH(node->parent->type) && node->value.str)
+ hashmap_remove(node->parent->compare.children, node->value.str);
+
+ free(node->value.str);
+ }
+
+ if (BUS_MATCH_IS_COMPARE(node->type)) {
+ assert(hashmap_isempty(node->compare.children));
+ hashmap_free(node->compare.children);
+ }
+
+ free(node);
+}
+
+static bool bus_match_node_maybe_free(struct bus_match_node *node) {
+ assert(node);
+
+ if (node->child)
+ return false;
+
+ if (BUS_MATCH_IS_COMPARE(node->type) && !hashmap_isempty(node->compare.children))
+ return true;
+
+ bus_match_node_free(node);
+ return true;
+}
+
+static bool value_node_test(
+ struct bus_match_node *node,
+ enum bus_match_node_type parent_type,
+ uint8_t value_u8,
+ const char *value_str,
+ sd_bus_message *m) {
+
+ assert(node);
+ assert(node->type == BUS_MATCH_VALUE);
+
+ /* Tests parameters against this value node, doing prefix
+ * magic and stuff. */
+
+ switch (parent_type) {
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ return node->value.u8 == value_u8;
+
+ case BUS_MATCH_SENDER:
+ if (streq_ptr(node->value.str, value_str))
+ return true;
+
+ if (m->creds.mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
+ char **i;
+
+ /* on kdbus we have the well known names list
+ * in the credentials, let's make use of that
+ * for an accurate match */
+
+ STRV_FOREACH(i, m->creds.well_known_names)
+ if (streq_ptr(node->value.str, *i))
+ return true;
+
+ } else {
+
+ /* If we don't have kdbus, we don't know the
+ * well-known names of the senders. In that,
+ * let's just hope that dbus-daemon doesn't
+ * send us stuff we didn't want. */
+
+ if (node->value.str[0] != ':' && value_str && value_str[0] == ':')
+ return true;
+ }
+
+ return false;
+
+ case BUS_MATCH_DESTINATION:
+ case BUS_MATCH_INTERFACE:
+ case BUS_MATCH_MEMBER:
+ case BUS_MATCH_PATH:
+ case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
+ return streq_ptr(node->value.str, value_str);
+
+ case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
+ return namespace_simple_pattern(node->value.str, value_str);
+
+ case BUS_MATCH_PATH_NAMESPACE:
+ return path_simple_pattern(node->value.str, value_str);
+
+ case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
+ return path_complex_pattern(node->value.str, value_str);
+
+ default:
+ assert_not_reached("Invalid node type");
+ }
+}
+
+static bool value_node_same(
+ struct bus_match_node *node,
+ enum bus_match_node_type parent_type,
+ uint8_t value_u8,
+ const char *value_str) {
+
+ /* Tests parameters against this value node, not doing prefix
+ * magic and stuff, i.e. this one actually compares the match
+ * itself.*/
+
+ assert(node);
+ assert(node->type == BUS_MATCH_VALUE);
+
+ switch (parent_type) {
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ return node->value.u8 == value_u8;
+
+ case BUS_MATCH_SENDER:
+ case BUS_MATCH_DESTINATION:
+ case BUS_MATCH_INTERFACE:
+ case BUS_MATCH_MEMBER:
+ case BUS_MATCH_PATH:
+ case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
+ case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
+ case BUS_MATCH_PATH_NAMESPACE:
+ case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
+ return streq(node->value.str, value_str);
+
+ default:
+ assert_not_reached("Invalid node type");
+ }
+}
+
+int bus_match_run(
+ sd_bus *bus,
+ struct bus_match_node *node,
+ sd_bus_message *m) {
+
+
+ const char *test_str = NULL;
+ uint8_t test_u8 = 0;
+ int r;
+
+ assert(m);
+
+ if (!node)
+ return 0;
+
+ if (bus && bus->match_callbacks_modified)
+ return 0;
+
+ /* Not these special semantics: when traversing the tree we
+ * usually let bus_match_run() when called for a node
+ * recursively invoke bus_match_run(). There's are two
+ * exceptions here though, which are BUS_NODE_ROOT (which
+ * cannot have a sibling), and BUS_NODE_VALUE (whose siblings
+ * are invoked anyway by its parent. */
+
+ switch (node->type) {
+
+ case BUS_MATCH_ROOT:
+
+ /* Run all children. Since we cannot have any siblings
+ * we won't call any. The children of the root node
+ * are compares or leaves, they will automatically
+ * call their siblings. */
+ return bus_match_run(bus, node->child, m);
+
+ case BUS_MATCH_VALUE:
+
+ /* Run all children. We don't execute any siblings, we
+ * assume our caller does that. The children of value
+ * nodes are compares or leaves, they will
+ * automatically call their siblings */
+
+ assert(node->child);
+ return bus_match_run(bus, node->child, m);
+
+ case BUS_MATCH_LEAF:
+
+ if (bus) {
+ if (node->leaf.last_iteration == bus->iteration_counter)
+ return 0;
+
+ node->leaf.last_iteration = bus->iteration_counter;
+ }
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ /* Run the callback. And then invoke siblings. */
+ if (node->leaf.callback) {
+ _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+
+ r = node->leaf.callback(bus, m, node->leaf.userdata, &error_buffer);
+ r = bus_maybe_reply_error(m, r, &error_buffer);
+ if (r != 0)
+ return r;
+ }
+
+ return bus_match_run(bus, node->next, m);
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ test_u8 = m->header->type;
+ break;
+
+ case BUS_MATCH_SENDER:
+ test_str = m->sender;
+ /* FIXME: resolve test_str from a well-known to a unique name first */
+ break;
+
+ case BUS_MATCH_DESTINATION:
+ test_str = m->destination;
+ break;
+
+ case BUS_MATCH_INTERFACE:
+ test_str = m->interface;
+ break;
+
+ case BUS_MATCH_MEMBER:
+ test_str = m->member;
+ break;
+
+ case BUS_MATCH_PATH:
+ case BUS_MATCH_PATH_NAMESPACE:
+ test_str = m->path;
+ break;
+
+ case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
+ test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG);
+ break;
+
+ case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
+ test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH);
+ break;
+
+ case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
+ test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE);
+ break;
+
+ default:
+ assert_not_reached("Unknown match type.");
+ }
+
+ if (BUS_MATCH_CAN_HASH(node->type)) {
+ struct bus_match_node *found;
+
+ /* Lookup via hash table, nice! So let's jump directly. */
+
+ if (test_str)
+ found = hashmap_get(node->compare.children, test_str);
+ else if (node->type == BUS_MATCH_MESSAGE_TYPE)
+ found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8));
+ else
+ found = NULL;
+
+ if (found) {
+ r = bus_match_run(bus, found, m);
+ if (r != 0)
+ return r;
+ }
+ } else {
+ struct bus_match_node *c;
+
+ /* No hash table, so let's iterate manually... */
+
+ for (c = node->child; c; c = c->next) {
+ if (!value_node_test(c, node->type, test_u8, test_str, m))
+ continue;
+
+ r = bus_match_run(bus, c, m);
+ if (r != 0)
+ return r;
+ }
+ }
+
+ if (bus && bus->match_callbacks_modified)
+ return 0;
+
+ /* And now, let's invoke our siblings */
+ return bus_match_run(bus, node->next, m);
+}
+
+static int bus_match_add_compare_value(
+ struct bus_match_node *where,
+ enum bus_match_node_type t,
+ uint8_t value_u8,
+ const char *value_str,
+ struct bus_match_node **ret) {
+
+ struct bus_match_node *c = NULL, *n = NULL;
+ int r;
+
+ assert(where);
+ assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
+ assert(BUS_MATCH_IS_COMPARE(t));
+ assert(ret);
+
+ for (c = where->child; c && c->type != t; c = c->next)
+ ;
+
+ if (c) {
+ /* Comparison node already exists? Then let's see if
+ * the value node exists too. */
+
+ if (t == BUS_MATCH_MESSAGE_TYPE)
+ n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
+ else if (BUS_MATCH_CAN_HASH(t))
+ n = hashmap_get(c->compare.children, value_str);
+ else {
+ for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
+ ;
+ }
+
+ if (n) {
+ *ret = n;
+ return 0;
+ }
+ } else {
+ /* Comparison node, doesn't exist yet? Then let's
+ * create it. */
+
+ c = new0(struct bus_match_node, 1);
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->type = t;
+ c->parent = where;
+ c->next = where->child;
+ if (c->next)
+ c->next->prev = c;
+ where->child = c;
+
+ if (t == BUS_MATCH_MESSAGE_TYPE) {
+ c->compare.children = hashmap_new(trivial_hash_func, trivial_compare_func);
+ if (!c->compare.children) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ } else if (BUS_MATCH_CAN_HASH(t)) {
+ c->compare.children = hashmap_new(string_hash_func, string_compare_func);
+ if (!c->compare.children) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+ }
+
+ n = new0(struct bus_match_node, 1);
+ if (!n) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ n->type = BUS_MATCH_VALUE;
+ n->value.u8 = value_u8;
+ if (value_str) {
+ n->value.str = strdup(value_str);
+ if (!n->value.str) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ n->parent = c;
+ if (c->compare.children) {
+
+ if (t == BUS_MATCH_MESSAGE_TYPE)
+ r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n);
+ else
+ r = hashmap_put(c->compare.children, n->value.str, n);
+
+ if (r < 0)
+ goto fail;
+ } else {
+ n->next = c->child;
+ if (n->next)
+ n->next->prev = n;
+ c->child = n;
+ }
+
+ *ret = n;
+ return 1;
+
+fail:
+ if (c)
+ bus_match_node_maybe_free(c);
+
+ if (n) {
+ free(n->value.str);
+ free(n);
+ }
+
+ return r;
+}
+
+static int bus_match_find_compare_value(
+ struct bus_match_node *where,
+ enum bus_match_node_type t,
+ uint8_t value_u8,
+ const char *value_str,
+ struct bus_match_node **ret) {
+
+ struct bus_match_node *c, *n;
+
+ assert(where);
+ assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
+ assert(BUS_MATCH_IS_COMPARE(t));
+ assert(ret);
+
+ for (c = where->child; c && c->type != t; c = c->next)
+ ;
+
+ if (!c)
+ return 0;
+
+ if (t == BUS_MATCH_MESSAGE_TYPE)
+ n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
+ else if (BUS_MATCH_CAN_HASH(t))
+ n = hashmap_get(c->compare.children, value_str);
+ else {
+ for (n = c->child; !value_node_same(n, t, value_u8, value_str); n = n->next)
+ ;
+ }
+
+ if (n) {
+ *ret = n;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bus_match_add_leaf(
+ struct bus_match_node *where,
+ sd_bus_message_handler_t callback,
+ void *userdata,
+ uint64_t cookie,
+ struct bus_match_node **ret) {
+
+ struct bus_match_node *n;
+
+ assert(where);
+ assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
+ assert(ret);
+
+ n = new0(struct bus_match_node, 1);
+ if (!n)
+ return -ENOMEM;
+
+ n->type = BUS_MATCH_LEAF;
+ n->parent = where;
+ n->next = where->child;
+ if (n->next)
+ n->next->prev = n;
+ n->leaf.callback = callback;
+ n->leaf.userdata = userdata;
+ n->leaf.cookie = cookie;
+
+ where->child = n;
+
+ *ret = n;
+ return 1;
+}
+
+static int bus_match_find_leaf(
+ struct bus_match_node *where,
+ sd_bus_message_handler_t callback,
+ void *userdata,
+ struct bus_match_node **ret) {
+
+ struct bus_match_node *c;
+
+ assert(where);
+ assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
+ assert(ret);
+
+ for (c = where->child; c; c = c->next) {
+ if (c->type == BUS_MATCH_LEAF &&
+ c->leaf.callback == callback &&
+ c->leaf.userdata == userdata) {
+ *ret = c;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) {
+ assert(k);
+
+ if (n == 4 && startswith(k, "type"))
+ return BUS_MATCH_MESSAGE_TYPE;
+ if (n == 6 && startswith(k, "sender"))
+ return BUS_MATCH_SENDER;
+ if (n == 11 && startswith(k, "destination"))
+ return BUS_MATCH_DESTINATION;
+ if (n == 9 && startswith(k, "interface"))
+ return BUS_MATCH_INTERFACE;
+ if (n == 6 && startswith(k, "member"))
+ return BUS_MATCH_MEMBER;
+ if (n == 4 && startswith(k, "path"))
+ return BUS_MATCH_PATH;
+ if (n == 14 && startswith(k, "path_namespace"))
+ return BUS_MATCH_PATH_NAMESPACE;
+
+ if (n == 4 && startswith(k, "arg")) {
+ int j;
+
+ j = undecchar(k[3]);
+ if (j < 0)
+ return -EINVAL;
+
+ return BUS_MATCH_ARG + j;
+ }
+
+ if (n == 5 && startswith(k, "arg")) {
+ int a, b;
+ enum bus_match_node_type t;
+
+ a = undecchar(k[3]);
+ b = undecchar(k[4]);
+ if (a <= 0 || b < 0)
+ return -EINVAL;
+
+ t = BUS_MATCH_ARG + a * 10 + b;
+ if (t > BUS_MATCH_ARG_LAST)
+ return -EINVAL;
+
+ return t;
+ }
+
+ if (n == 8 && startswith(k, "arg") && startswith(k + 4, "path")) {
+ int j;
+
+ j = undecchar(k[3]);
+ if (j < 0)
+ return -EINVAL;
+
+ return BUS_MATCH_ARG_PATH + j;
+ }
+
+ if (n == 9 && startswith(k, "arg") && startswith(k + 5, "path")) {
+ enum bus_match_node_type t;
+ int a, b;
+
+ a = undecchar(k[3]);
+ b = undecchar(k[4]);
+ if (a <= 0 || b < 0)
+ return -EINVAL;
+
+ t = BUS_MATCH_ARG_PATH + a * 10 + b;
+ if (t > BUS_MATCH_ARG_PATH_LAST)
+ return -EINVAL;
+
+ return t;
+ }
+
+ if (n == 13 && startswith(k, "arg") && startswith(k + 4, "namespace")) {
+ int j;
+
+ j = undecchar(k[3]);
+ if (j < 0)
+ return -EINVAL;
+
+ return BUS_MATCH_ARG_NAMESPACE + j;
+ }
+
+ if (n == 14 && startswith(k, "arg") && startswith(k + 5, "namespace")) {
+ enum bus_match_node_type t;
+ int a, b;
+
+ a = undecchar(k[3]);
+ b = undecchar(k[4]);
+ if (a <= 0 || b < 0)
+ return -EINVAL;
+
+ t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b;
+ if (t > BUS_MATCH_ARG_NAMESPACE_LAST)
+ return -EINVAL;
+
+ return t;
+ }
+
+ return -EINVAL;
+}
+
+static int match_component_compare(const void *a, const void *b) {
+ const struct bus_match_component *x = a, *y = b;
+
+ if (x->type < y->type)
+ return -1;
+ if (x->type > y->type)
+ return 1;
+
+ return 0;
+}
+
+void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) {
+ unsigned i;
+
+ for (i = 0; i < n_components; i++)
+ free(components[i].value_str);
+
+ free(components);
+}
+
+int bus_match_parse(
+ const char *match,
+ struct bus_match_component **_components,
+ unsigned *_n_components) {
+
+ const char *p = match;
+ struct bus_match_component *components = NULL;
+ size_t components_allocated = 0;
+ unsigned n_components = 0, i;
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ assert(match);
+ assert(_components);
+ assert(_n_components);
+
+ while (*p != 0) {
+ const char *eq, *q;
+ enum bus_match_node_type t;
+ unsigned j = 0;
+ size_t value_allocated = 0;
+ bool escaped = false, quoted;
+ uint8_t u;
+
+ eq = strchr(p, '=');
+ if (!eq)
+ return -EINVAL;
+
+ t = bus_match_node_type_from_string(p, eq - p);
+ if (t < 0)
+ return -EINVAL;
+
+ quoted = eq[1] == '\'';
+
+ for (q = eq + 1 + quoted;; q++) {
+
+ if (*q == 0) {
+
+ if (quoted) {
+ r = -EINVAL;
+ goto fail;
+ } else {
+ if (value)
+ value[j] = 0;
+ break;
+ }
+ }
+
+ if (!escaped) {
+ if (*q == '\\') {
+ escaped = true;
+ continue;
+ }
+
+ if (quoted) {
+ if (*q == '\'') {
+ if (value)
+ value[j] = 0;
+ break;
+ }
+ } else {
+ if (*q == ',') {
+ if (value)
+ value[j] = 0;
+
+ break;
+ }
+ }
+ }
+
+ if (!GREEDY_REALLOC(value, value_allocated, j + 2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[j++] = *q;
+ escaped = false;
+ }
+
+ if (!value) {
+ value = strdup("");
+ if (!value) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (t == BUS_MATCH_MESSAGE_TYPE) {
+ r = bus_message_type_from_string(value, &u);
+ if (r < 0)
+ goto fail;
+
+ free(value);
+ value = NULL;
+ } else
+ u = 0;
+
+ if (!GREEDY_REALLOC(components, components_allocated, n_components + 1)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ components[n_components].type = t;
+ components[n_components].value_str = value;
+ components[n_components].value_u8 = u;
+ n_components++;
+
+ value = NULL;
+
+ if (q[1] == 0)
+ break;
+
+ if (q[quoted] != ',') {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ p = q + 1 + quoted;
+ }
+
+ /* Order the whole thing, so that we always generate the same tree */
+ qsort_safe(components, n_components, sizeof(struct bus_match_component), match_component_compare);
+
+ /* Check for duplicates */
+ for (i = 0; i+1 < n_components; i++)
+ if (components[i].type == components[i+1].type) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ *_components = components;
+ *_n_components = n_components;
+
+ return 0;
+
+fail:
+ bus_match_parse_free(components, n_components);
+ return r;
+}
+
+char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) {
+ _cleanup_free_ FILE *f = NULL;
+ char *buffer = NULL;
+ size_t size = 0;
+ unsigned i;
+
+ if (n_components <= 0)
+ return strdup("");
+
+ assert(components);
+
+ f = open_memstream(&buffer, &size);
+ if (!f)
+ return NULL;
+
+ for (i = 0; i < n_components; i++) {
+ char buf[32];
+
+ if (i != 0)
+ fputc(',', f);
+
+ fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f);
+ fputc('=', f);
+ fputc('\'', f);
+
+ if (components[i].type == BUS_MATCH_MESSAGE_TYPE)
+ fputs(bus_message_type_to_string(components[i].value_u8), f);
+ else
+ fputs(components[i].value_str, f);
+
+ fputc('\'', f);
+ }
+
+ fflush(f);
+ if (ferror(f))
+ return NULL;
+
+ return buffer;
+}
+
+int bus_match_add(
+ struct bus_match_node *root,
+ struct bus_match_component *components,
+ unsigned n_components,
+ sd_bus_message_handler_t callback,
+ void *userdata,
+ uint64_t cookie,
+ struct bus_match_node **ret) {
+
+ unsigned i;
+ struct bus_match_node *n;
+ int r;
+
+ assert(root);
+
+ n = root;
+ for (i = 0; i < n_components; i++) {
+ r = bus_match_add_compare_value(
+ n, components[i].type,
+ components[i].value_u8, components[i].value_str, &n);
+ if (r < 0)
+ return r;
+ }
+
+ r = bus_match_add_leaf(n, callback, userdata, cookie, &n);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = n;
+
+ return 0;
+}
+
+int bus_match_remove(
+ struct bus_match_node *root,
+ struct bus_match_component *components,
+ unsigned n_components,
+ sd_bus_message_handler_t callback,
+ void *userdata,
+ uint64_t *cookie) {
+
+ unsigned i;
+ struct bus_match_node *n, **gc;
+ int r;
+
+ assert(root);
+
+ gc = newa(struct bus_match_node*, n_components);
+
+ n = root;
+ for (i = 0; i < n_components; i++) {
+ r = bus_match_find_compare_value(
+ n, components[i].type,
+ components[i].value_u8, components[i].value_str,
+ &n);
+ if (r <= 0)
+ return r;
+
+ gc[i] = n;
+ }
+
+ r = bus_match_find_leaf(n, callback, userdata, &n);
+ if (r <= 0)
+ return r;
+
+ if (cookie)
+ *cookie = n->leaf.cookie;
+
+ /* Free the leaf */
+ bus_match_node_free(n);
+
+ /* Prune the tree above */
+ for (i = n_components; i > 0; i --) {
+ struct bus_match_node *p = gc[i-1]->parent;
+
+ if (!bus_match_node_maybe_free(gc[i-1]))
+ break;
+
+ if (!bus_match_node_maybe_free(p))
+ break;
+ }
+
+ return r;
+}
+
+void bus_match_free(struct bus_match_node *node) {
+ struct bus_match_node *c;
+
+ if (!node)
+ return;
+
+ if (BUS_MATCH_CAN_HASH(node->type)) {
+ Iterator i;
+
+ HASHMAP_FOREACH(c, node->compare.children, i)
+ bus_match_free(c);
+
+ assert(hashmap_isempty(node->compare.children));
+ }
+
+ while ((c = node->child))
+ bus_match_free(c);
+
+ if (node->type != BUS_MATCH_ROOT)
+ bus_match_node_free(node);
+}
+
+const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) {
+ switch (t) {
+
+ case BUS_MATCH_ROOT:
+ return "root";
+
+ case BUS_MATCH_VALUE:
+ return "value";
+
+ case BUS_MATCH_LEAF:
+ return "leaf";
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ return "type";
+
+ case BUS_MATCH_SENDER:
+ return "sender";
+
+ case BUS_MATCH_DESTINATION:
+ return "destination";
+
+ case BUS_MATCH_INTERFACE:
+ return "interface";
+
+ case BUS_MATCH_MEMBER:
+ return "member";
+
+ case BUS_MATCH_PATH:
+ return "path";
+
+ case BUS_MATCH_PATH_NAMESPACE:
+ return "path_namespace";
+
+ case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
+ snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
+ return buf;
+
+ case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
+ snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
+ return buf;
+
+ case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
+ snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
+ return buf;
+
+ default:
+ return NULL;
+ }
+}
+
+void bus_match_dump(struct bus_match_node *node, unsigned level) {
+ struct bus_match_node *c;
+ _cleanup_free_ char *pfx = NULL;
+ char buf[32];
+
+ if (!node)
+ return;
+
+ pfx = strrep(" ", level);
+ printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf)));
+
+ if (node->type == BUS_MATCH_VALUE) {
+ if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
+ printf(" <%u>\n", node->value.u8);
+ else
+ printf(" <%s>\n", node->value.str);
+ } else if (node->type == BUS_MATCH_ROOT)
+ puts(" root");
+ else if (node->type == BUS_MATCH_LEAF)
+ printf(" %p/%p\n", node->leaf.callback, node->leaf.userdata);
+ else
+ putchar('\n');
+
+ if (BUS_MATCH_CAN_HASH(node->type)) {
+ Iterator i;
+
+ HASHMAP_FOREACH(c, node->compare.children, i)
+ bus_match_dump(c, level + 1);
+ }
+
+ for (c = node->child; c; c = c->next)
+ bus_match_dump(c, level + 1);
+}
diff --git a/src/libsystemd/sd-bus/bus-match.h b/src/libsystemd/sd-bus/bus-match.h
new file mode 100644
index 0000000..056082b
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-match.h
@@ -0,0 +1,93 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "hashmap.h"
+
+#include "sd-bus.h"
+
+enum bus_match_node_type {
+ BUS_MATCH_ROOT,
+ BUS_MATCH_VALUE,
+ BUS_MATCH_LEAF,
+
+ /* The following are all different kinds of compare nodes */
+ BUS_MATCH_SENDER,
+ BUS_MATCH_MESSAGE_TYPE,
+ BUS_MATCH_DESTINATION,
+ BUS_MATCH_INTERFACE,
+ BUS_MATCH_MEMBER,
+ BUS_MATCH_PATH,
+ BUS_MATCH_PATH_NAMESPACE,
+ BUS_MATCH_ARG,
+ BUS_MATCH_ARG_LAST = BUS_MATCH_ARG + 63,
+ BUS_MATCH_ARG_PATH,
+ BUS_MATCH_ARG_PATH_LAST = BUS_MATCH_ARG_PATH + 63,
+ BUS_MATCH_ARG_NAMESPACE,
+ BUS_MATCH_ARG_NAMESPACE_LAST = BUS_MATCH_ARG_NAMESPACE + 63,
+ _BUS_MATCH_NODE_TYPE_MAX,
+ _BUS_MATCH_NODE_TYPE_INVALID = -1
+};
+
+struct bus_match_node {
+ enum bus_match_node_type type;
+ struct bus_match_node *parent, *next, *prev, *child;
+
+ union {
+ struct {
+ char *str;
+ uint8_t u8;
+ } value;
+ struct {
+ sd_bus_message_handler_t callback;
+ void *userdata;
+ unsigned last_iteration;
+ uint64_t cookie;
+ } leaf;
+ struct {
+ /* If this is set, then the child is NULL */
+ Hashmap *children;
+ } compare;
+ };
+};
+
+struct bus_match_component {
+ enum bus_match_node_type type;
+ uint8_t value_u8;
+ char *value_str;
+};
+
+int bus_match_run(sd_bus *bus, struct bus_match_node *root, sd_bus_message *m);
+
+int bus_match_add(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, sd_bus_message_handler_t callback, void *userdata, uint64_t cookie, struct bus_match_node **ret);
+int bus_match_remove(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, sd_bus_message_handler_t callback, void *userdata, uint64_t *cookie);
+
+void bus_match_free(struct bus_match_node *node);
+
+void bus_match_dump(struct bus_match_node *node, unsigned level);
+
+const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l);
+enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n);
+
+int bus_match_parse(const char *match, struct bus_match_component **_components, unsigned *_n_components);
+void bus_match_parse_free(struct bus_match_component *components, unsigned n_components);
+char *bus_match_to_string(struct bus_match_component *components, unsigned n_components);
diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c
new file mode 100644
index 0000000..1a2039b
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-message.c
@@ -0,0 +1,5535 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "util.h"
+#include "utf8.h"
+#include "strv.h"
+#include "time-util.h"
+#include "cgroup-util.h"
+
+#include "sd-bus.h"
+#include "bus-message.h"
+#include "bus-internal.h"
+#include "bus-type.h"
+#include "bus-signature.h"
+#include "bus-gvariant.h"
+#include "bus-util.h"
+
+static int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored);
+
+static void *adjust_pointer(const void *p, void *old_base, size_t sz, void *new_base) {
+
+ if (p == NULL)
+ return NULL;
+
+ if (old_base == new_base)
+ return (void*) p;
+
+ if ((uint8_t*) p < (uint8_t*) old_base)
+ return (void*) p;
+
+ if ((uint8_t*) p >= (uint8_t*) old_base + sz)
+ return (void*) p;
+
+ return (uint8_t*) new_base + ((uint8_t*) p - (uint8_t*) old_base);
+}
+
+static void message_free_part(sd_bus_message *m, struct bus_body_part *part) {
+ assert(m);
+ assert(part);
+
+ if (part->memfd >= 0) {
+ /* If we can reuse the memfd, try that. For that it
+ * can't be sealed yet. */
+
+ if (!part->sealed)
+ bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped, part->allocated);
+ else {
+ if (part->mapped > 0)
+ assert_se(munmap(part->data, part->mapped) == 0);
+
+ close_nointr_nofail(part->memfd);
+ }
+
+ } else if (part->munmap_this)
+ munmap(part->data, part->mapped);
+ else if (part->free_this)
+ free(part->data);
+
+ if (part != &m->body)
+ free(part);
+}
+
+static void message_reset_parts(sd_bus_message *m) {
+ struct bus_body_part *part;
+
+ assert(m);
+
+ part = &m->body;
+ while (m->n_body_parts > 0) {
+ struct bus_body_part *next = part->next;
+ message_free_part(m, part);
+ part = next;
+ m->n_body_parts--;
+ }
+
+ m->body_end = NULL;
+
+ m->cached_rindex_part = NULL;
+ m->cached_rindex_part_begin = 0;
+}
+
+static void message_reset_containers(sd_bus_message *m) {
+ unsigned i;
+
+ assert(m);
+
+ for (i = 0; i < m->n_containers; i++) {
+ free(m->containers[i].signature);
+ free(m->containers[i].offsets);
+ }
+
+ free(m->containers);
+ m->containers = NULL;
+
+ m->n_containers = m->containers_allocated = 0;
+ m->root_container.index = 0;
+}
+
+static void message_free(sd_bus_message *m) {
+ assert(m);
+
+ if (m->free_header)
+ free(m->header);
+
+ message_reset_parts(m);
+
+ if (m->free_kdbus)
+ free(m->kdbus);
+
+ if (m->release_kdbus) {
+ uint64_t off;
+
+ off = (uint8_t *)m->kdbus - (uint8_t *)m->bus->kdbus_buffer;
+ ioctl(m->bus->input_fd, KDBUS_CMD_FREE, &off);
+ }
+
+ if (m->bus)
+ sd_bus_unref(m->bus);
+
+ if (m->free_fds) {
+ close_many(m->fds, m->n_fds);
+ free(m->fds);
+ }
+
+ if (m->iovec != m->iovec_fixed)
+ free(m->iovec);
+
+ message_reset_containers(m);
+ free(m->root_container.signature);
+ free(m->root_container.offsets);
+
+ free(m->root_container.peeked_signature);
+
+ bus_creds_done(&m->creds);
+ free(m);
+}
+
+static void *message_extend_fields(sd_bus_message *m, size_t align, size_t sz, bool add_offset) {
+ void *op, *np;
+ size_t old_size, new_size, start;
+
+ assert(m);
+
+ if (m->poisoned)
+ return NULL;
+
+ old_size = sizeof(struct bus_header) + m->header->fields_size;
+ start = ALIGN_TO(old_size, align);
+ new_size = start + sz;
+
+ if (old_size == new_size)
+ return (uint8_t*) m->header + old_size;
+
+ if (new_size > (size_t) ((uint32_t) -1))
+ goto poison;
+
+ if (m->free_header) {
+ np = realloc(m->header, ALIGN8(new_size));
+ if (!np)
+ goto poison;
+ } else {
+ /* Initially, the header is allocated as part of of
+ * the sd_bus_message itself, let's replace it by
+ * dynamic data */
+
+ np = malloc(ALIGN8(new_size));
+ if (!np)
+ goto poison;
+
+ memcpy(np, m->header, sizeof(struct bus_header));
+ }
+
+ /* Zero out padding */
+ if (start > old_size)
+ memset((uint8_t*) np + old_size, 0, start - old_size);
+
+ op = m->header;
+ m->header = np;
+ m->header->fields_size = new_size - sizeof(struct bus_header);
+
+ /* Adjust quick access pointers */
+ m->path = adjust_pointer(m->path, op, old_size, m->header);
+ m->interface = adjust_pointer(m->interface, op, old_size, m->header);
+ m->member = adjust_pointer(m->member, op, old_size, m->header);
+ m->destination = adjust_pointer(m->destination, op, old_size, m->header);
+ m->sender = adjust_pointer(m->sender, op, old_size, m->header);
+ m->error.name = adjust_pointer(m->error.name, op, old_size, m->header);
+
+ m->free_header = true;
+
+ if (add_offset) {
+ if (m->n_header_offsets >= ELEMENTSOF(m->header_offsets))
+ goto poison;
+
+ m->header_offsets[m->n_header_offsets++] = new_size - sizeof(struct bus_header);
+ }
+
+ return (uint8_t*) np + start;
+
+poison:
+ m->poisoned = true;
+ return NULL;
+}
+
+static int message_append_field_string(
+ sd_bus_message *m,
+ uint8_t h,
+ char type,
+ const char *s,
+ const char **ret) {
+
+ size_t l;
+ uint8_t *p;
+
+ assert(m);
+
+ /* dbus1 doesn't allow strings over 32bit, let's enforce this
+ * globally, to not risk convertability */
+ l = strlen(s);
+ if (l > (size_t) (uint32_t) -1)
+ return -EINVAL;
+
+ /* Signature "(yv)" where the variant contains "s" */
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+
+ /* (field id byte + 7x padding, ((string + NUL) + NUL + signature string 's') */
+ p = message_extend_fields(m, 8, 1 + 7 + l + 1 + 1 + 1, true);
+ if (!p)
+ return -ENOMEM;
+
+ p[0] = h;
+ memset(p+1, 0, 7);
+ memcpy(p+8, s, l);
+ p[8+l] = 0;
+ p[8+l+1] = 0;
+ p[8+l+2] = type;
+
+ if (ret)
+ *ret = (char*) p + 8;
+
+ } else {
+ /* (field id byte + (signature length + signature 's' + NUL) + (string length + string + NUL)) */
+ p = message_extend_fields(m, 8, 4 + 4 + l + 1, false);
+ if (!p)
+ return -ENOMEM;
+
+ p[0] = h;
+ p[1] = 1;
+ p[2] = type;
+ p[3] = 0;
+
+ ((uint32_t*) p)[1] = l;
+ memcpy(p + 8, s, l + 1);
+
+ if (ret)
+ *ret = (char*) p + 8;
+ }
+
+ return 0;
+}
+
+static int message_append_field_signature(
+ sd_bus_message *m,
+ uint8_t h,
+ const char *s,
+ const char **ret) {
+
+ size_t l;
+ uint8_t *p;
+
+ assert(m);
+
+ /* dbus1 doesn't allow signatures over 32bit, let's enforce
+ * this globally, to not risk convertability */
+ l = strlen(s);
+ if (l > 255)
+ return -EINVAL;
+
+ /* Signature "(yv)" where the variant contains "g" */
+
+ if (BUS_MESSAGE_IS_GVARIANT(m))
+ /* For gvariant the serialization is the same as for normal strings */
+ return message_append_field_string(m, h, 'g', s, ret);
+ else {
+ /* (field id byte + (signature length + signature 'g' + NUL) + (string length + string + NUL)) */
+ p = message_extend_fields(m, 8, 4 + 1 + l + 1, false);
+ if (!p)
+ return -ENOMEM;
+
+ p[0] = h;
+ p[1] = 1;
+ p[2] = SD_BUS_TYPE_SIGNATURE;
+ p[3] = 0;
+ p[4] = l;
+ memcpy(p + 5, s, l + 1);
+
+ if (ret)
+ *ret = (const char*) p + 5;
+ }
+
+ return 0;
+}
+
+static int message_append_field_uint32(sd_bus_message *m, uint8_t h, uint32_t x) {
+ uint8_t *p;
+
+ assert(m);
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ /* (field id byte + 7x padding + ((value + NUL + signature string 'u') */
+
+ p = message_extend_fields(m, 8, 1 + 7 + 4 + 1 + 1, true);
+ if (!p)
+ return -ENOMEM;
+
+ p[0] = h;
+ memset(p+1, 0, 7);
+ *((uint32_t*) (p + 8)) = x;
+ p[12] = 0;
+ p[13] = 'u';
+ } else {
+ /* (field id byte + (signature length + signature 'u' + NUL) + value) */
+ p = message_extend_fields(m, 8, 4 + 4, false);
+ if (!p)
+ return -ENOMEM;
+
+ p[0] = h;
+ p[1] = 1;
+ p[2] = SD_BUS_TYPE_UINT32;
+ p[3] = 0;
+
+ ((uint32_t*) p)[1] = x;
+ }
+
+ return 0;
+}
+
+int bus_message_from_header(
+ sd_bus *bus,
+ void *buffer,
+ size_t length,
+ int *fds,
+ unsigned n_fds,
+ const struct ucred *ucred,
+ const char *label,
+ size_t extra,
+ sd_bus_message **ret) {
+
+ sd_bus_message *m;
+ struct bus_header *h;
+ size_t a, label_sz;
+
+ assert(buffer || length <= 0);
+ assert(fds || n_fds <= 0);
+ assert(ret);
+
+ if (length < sizeof(struct bus_header))
+ return -EBADMSG;
+
+ h = buffer;
+ if (h->version != 1 &&
+ h->version != 2)
+ return -EBADMSG;
+
+ if (h->serial == 0)
+ return -EBADMSG;
+
+ if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID)
+ return -EBADMSG;
+
+ if (h->endian != BUS_LITTLE_ENDIAN &&
+ h->endian != BUS_BIG_ENDIAN)
+ return -EBADMSG;
+
+ a = ALIGN(sizeof(sd_bus_message)) + ALIGN(extra);
+
+ if (label) {
+ label_sz = strlen(label);
+ a += label_sz + 1;
+ }
+
+ m = malloc0(a);
+ if (!m)
+ return -ENOMEM;
+
+ m->n_ref = 1;
+ m->sealed = true;
+ m->header = h;
+ m->fds = fds;
+ m->n_fds = n_fds;
+
+ if (ucred) {
+ m->creds.uid = ucred->uid;
+ m->creds.pid = ucred->pid;
+ m->creds.gid = ucred->gid;
+ m->creds.mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_PID | SD_BUS_CREDS_GID;
+ }
+
+ if (label) {
+ m->creds.label = (char*) m + ALIGN(sizeof(sd_bus_message)) + ALIGN(extra);
+ memcpy(m->creds.label, label, label_sz + 1);
+
+ m->creds.mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
+ }
+
+ if (bus)
+ m->bus = sd_bus_ref(bus);
+
+ *ret = m;
+ return 0;
+}
+
+int bus_message_from_malloc(
+ sd_bus *bus,
+ void *buffer,
+ size_t length,
+ int *fds,
+ unsigned n_fds,
+ const struct ucred *ucred,
+ const char *label,
+ sd_bus_message **ret) {
+
+ sd_bus_message *m;
+ size_t sz;
+ int r;
+
+ r = bus_message_from_header(bus, buffer, length, fds, n_fds, ucred, label, 0, &m);
+ if (r < 0)
+ return r;
+
+ if (length != BUS_MESSAGE_SIZE(m)) {
+ r = -EBADMSG;
+ goto fail;
+ }
+
+ sz = length - sizeof(struct bus_header) - ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m));
+ if (sz > 0) {
+ m->n_body_parts = 1;
+ m->body.data = (uint8_t*) buffer + sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m));
+ m->body.size = sz;
+ m->body.sealed = true;
+ m->body.memfd = -1;
+ }
+
+ m->n_iovec = 1;
+ m->iovec = m->iovec_fixed;
+ m->iovec[0].iov_base = buffer;
+ m->iovec[0].iov_len = length;
+
+ r = bus_message_parse_fields(m);
+ if (r < 0)
+ goto fail;
+
+ /* We take possession of the memory and fds now */
+ m->free_header = true;
+ m->free_fds = true;
+
+ *ret = m;
+ return 0;
+
+fail:
+ message_free(m);
+ return r;
+}
+
+static sd_bus_message *message_new(sd_bus *bus, uint8_t type) {
+ sd_bus_message *m;
+
+ m = malloc0(ALIGN(sizeof(sd_bus_message)) + sizeof(struct bus_header));
+ if (!m)
+ return NULL;
+
+ m->n_ref = 1;
+ m->header = (struct bus_header*) ((uint8_t*) m + ALIGN(sizeof(struct sd_bus_message)));
+ m->header->endian = BUS_NATIVE_ENDIAN;
+ m->header->type = type;
+ m->header->version = bus ? bus->message_version : 1;
+ m->allow_fds = !bus || bus->can_fds || (bus->state != BUS_HELLO && bus->state != BUS_RUNNING);
+ m->root_container.need_offsets = BUS_MESSAGE_IS_GVARIANT(m);
+
+ if (bus)
+ m->bus = sd_bus_ref(bus);
+
+ return m;
+}
+
+_public_ int sd_bus_message_new_signal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ assert_return(!bus || bus->state != BUS_UNSET, -ENOTCONN);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(interface_name_is_valid(interface), -EINVAL);
+ assert_return(member_name_is_valid(member), -EINVAL);
+ assert_return(m, -EINVAL);
+
+ t = message_new(bus, SD_BUS_MESSAGE_SIGNAL);
+ if (!t)
+ return -ENOMEM;
+
+ t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
+
+ r = message_append_field_string(t, BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path);
+ if (r < 0)
+ goto fail;
+ r = message_append_field_string(t, BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface);
+ if (r < 0)
+ goto fail;
+ r = message_append_field_string(t, BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member);
+ if (r < 0)
+ goto fail;
+
+ *m = t;
+ return 0;
+
+fail:
+ sd_bus_message_unref(t);
+ return r;
+}
+
+_public_ int sd_bus_message_new_method_call(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ assert_return(!bus || bus->state != BUS_UNSET, -ENOTCONN);
+ assert_return(!destination || service_name_is_valid(destination), -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(!interface || interface_name_is_valid(interface), -EINVAL);
+ assert_return(member_name_is_valid(member), -EINVAL);
+ assert_return(m, -EINVAL);
+
+ t = message_new(bus, SD_BUS_MESSAGE_METHOD_CALL);
+ if (!t)
+ return -ENOMEM;
+
+ r = message_append_field_string(t, BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path);
+ if (r < 0)
+ goto fail;
+ r = message_append_field_string(t, BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member);
+ if (r < 0)
+ goto fail;
+
+ if (interface) {
+ r = message_append_field_string(t, BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface);
+ if (r < 0)
+ goto fail;
+ }
+
+ if (destination) {
+ r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &t->destination);
+ if (r < 0)
+ goto fail;
+ }
+
+ *m = t;
+ return 0;
+
+fail:
+ message_free(t);
+ return r;
+}
+
+static int message_new_reply(
+ sd_bus_message *call,
+ uint8_t type,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ assert_return(call, -EINVAL);
+ assert_return(call->sealed, -EPERM);
+ assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
+ assert_return(!call->bus || call->bus->state != BUS_UNSET, -ENOTCONN);
+ assert_return(m, -EINVAL);
+
+ t = message_new(call->bus, type);
+ if (!t)
+ return -ENOMEM;
+
+ t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
+ t->reply_cookie = BUS_MESSAGE_COOKIE(call);
+
+ r = message_append_field_uint32(t, BUS_MESSAGE_HEADER_REPLY_SERIAL, t->reply_cookie);
+ if (r < 0)
+ goto fail;
+
+ if (call->sender) {
+ r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, call->sender, &t->destination);
+ if (r < 0)
+ goto fail;
+ }
+
+ t->dont_send = !!(call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED);
+ t->enforced_reply_signature = call->enforced_reply_signature;
+
+ *m = t;
+ return 0;
+
+fail:
+ message_free(t);
+ return r;
+}
+
+_public_ int sd_bus_message_new_method_return(
+ sd_bus_message *call,
+ sd_bus_message **m) {
+
+ return message_new_reply(call, SD_BUS_MESSAGE_METHOD_RETURN, m);
+}
+
+_public_ int sd_bus_message_new_method_error(
+ sd_bus_message *call,
+ const sd_bus_error *e,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ assert_return(sd_bus_error_is_set(e), -EINVAL);
+ assert_return(m, -EINVAL);
+
+ r = message_new_reply(call, SD_BUS_MESSAGE_METHOD_ERROR, &t);
+ if (r < 0)
+ return r;
+
+ r = message_append_field_string(t, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, e->name, &t->error.name);
+ if (r < 0)
+ goto fail;
+
+ if (e->message) {
+ r = message_append_basic(t, SD_BUS_TYPE_STRING, e->message, (const void**) &t->error.message);
+ if (r < 0)
+ goto fail;
+ }
+
+ t->error._need_free = -1;
+
+ *m = t;
+ return 0;
+
+fail:
+ message_free(t);
+ return r;
+}
+
+_public_ int sd_bus_message_new_method_errorf(
+ sd_bus_message *call,
+ sd_bus_message **m,
+ const char *name,
+ const char *format,
+ ...) {
+
+ _cleanup_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ va_list ap;
+
+ assert_return(name, -EINVAL);
+ assert_return(m, -EINVAL);
+
+ va_start(ap, format);
+ bus_error_setfv(&error, name, format, ap);
+ va_end(ap);
+
+ return sd_bus_message_new_method_error(call, &error, m);
+}
+
+_public_ int sd_bus_message_new_method_errno(
+ sd_bus_message *call,
+ int error,
+ const sd_bus_error *p,
+ sd_bus_message **m) {
+
+ _cleanup_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
+
+ if (sd_bus_error_is_set(p))
+ return sd_bus_message_new_method_error(call, p, m);
+
+ sd_bus_error_set_errno(&berror, error);
+
+ return sd_bus_message_new_method_error(call, &berror, m);
+}
+
+_public_ int sd_bus_message_new_method_errnof(
+ sd_bus_message *call,
+ sd_bus_message **m,
+ int error,
+ const char *format,
+ ...) {
+
+ _cleanup_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
+ va_list ap;
+
+ va_start(ap, format);
+ bus_error_set_errnofv(&berror, error, format, ap);
+ va_end(ap);
+
+ return sd_bus_message_new_method_error(call, &berror, m);
+}
+
+int bus_message_new_synthetic_error(
+ sd_bus *bus,
+ uint64_t cookie,
+ const sd_bus_error *e,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ assert(sd_bus_error_is_set(e));
+ assert(m);
+
+ t = message_new(bus, SD_BUS_MESSAGE_METHOD_ERROR);
+ if (!t)
+ return -ENOMEM;
+
+ t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
+ t->reply_cookie = cookie;
+
+ r = message_append_field_uint32(t, BUS_MESSAGE_HEADER_REPLY_SERIAL, t->reply_cookie);
+ if (r < 0)
+ goto fail;
+
+ if (bus && bus->unique_name) {
+ r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, bus->unique_name, &t->destination);
+ if (r < 0)
+ goto fail;
+ }
+
+ r = message_append_field_string(t, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, e->name, &t->error.name);
+ if (r < 0)
+ goto fail;
+
+ if (e->message) {
+ r = message_append_basic(t, SD_BUS_TYPE_STRING, e->message, (const void**) &t->error.message);
+ if (r < 0)
+ goto fail;
+ }
+
+ t->error._need_free = -1;
+
+ *m = t;
+ return 0;
+
+fail:
+ message_free(t);
+ return r;
+}
+
+_public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) {
+ assert_return(m, NULL);
+
+ assert(m->n_ref > 0);
+ m->n_ref++;
+
+ return m;
+}
+
+_public_ sd_bus_message* sd_bus_message_unref(sd_bus_message *m) {
+
+ if (!m)
+ return NULL;
+
+ assert(m->n_ref > 0);
+ m->n_ref--;
+
+ if (m->n_ref <= 0)
+ message_free(m);
+
+ return NULL;
+}
+
+_public_ int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type) {
+ assert_return(m, -EINVAL);
+ assert_return(type, -EINVAL);
+
+ *type = m->header->type;
+ return 0;
+}
+
+_public_ int sd_bus_message_get_cookie(sd_bus_message *m, uint64_t *cookie) {
+ assert_return(m, -EINVAL);
+ assert_return(cookie, -EINVAL);
+ assert_return(m->header->serial != 0, -ENODATA);
+
+ *cookie = BUS_MESSAGE_COOKIE(m);
+ return 0;
+}
+
+_public_ int sd_bus_message_get_reply_cookie(sd_bus_message *m, uint64_t *cookie) {
+ assert_return(m, -EINVAL);
+ assert_return(cookie, -EINVAL);
+ assert_return(m->reply_cookie != 0, -ENODATA);
+
+ *cookie = m->reply_cookie;
+ return 0;
+}
+
+_public_ int sd_bus_message_get_no_reply(sd_bus_message *m) {
+ assert_return(m, -EINVAL);
+
+ return m->header->type == SD_BUS_MESSAGE_METHOD_CALL ? !!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) : 0;
+}
+
+_public_ int sd_bus_message_get_no_auto_start(sd_bus_message *m) {
+ assert_return(m, -EINVAL);
+
+ return !!(m->header->flags & BUS_MESSAGE_NO_AUTO_START);
+}
+
+_public_ const char *sd_bus_message_get_path(sd_bus_message *m) {
+ assert_return(m, NULL);
+
+ return m->path;
+}
+
+_public_ const char *sd_bus_message_get_interface(sd_bus_message *m) {
+ assert_return(m, NULL);
+
+ return m->interface;
+}
+
+_public_ const char *sd_bus_message_get_member(sd_bus_message *m) {
+ assert_return(m, NULL);
+
+ return m->member;
+}
+
+_public_ const char *sd_bus_message_get_destination(sd_bus_message *m) {
+ assert_return(m, NULL);
+
+ return m->destination;
+}
+
+_public_ const char *sd_bus_message_get_sender(sd_bus_message *m) {
+ assert_return(m, NULL);
+
+ return m->sender;
+}
+
+_public_ const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m) {
+ assert_return(m, NULL);
+ assert_return(sd_bus_error_is_set(&m->error), NULL);
+
+ return &m->error;
+}
+
+_public_ int sd_bus_message_get_monotonic_timestamp(sd_bus_message *m, uint64_t *usec) {
+ assert_return(m, -EINVAL);
+ assert_return(usec, -EINVAL);
+ assert_return(m->monotonic > 0, -ENODATA);
+
+ *usec = m->monotonic;
+ return 0;
+}
+
+_public_ int sd_bus_message_get_realtime_timestamp(sd_bus_message *m, uint64_t *usec) {
+ assert_return(m, -EINVAL);
+ assert_return(usec, -EINVAL);
+ assert_return(m->realtime > 0, -ENODATA);
+
+ *usec = m->realtime;
+ return 0;
+}
+
+_public_ sd_bus_creds *sd_bus_message_get_creds(sd_bus_message *m) {
+ assert_return(m, NULL);
+
+ if (m->creds.mask == 0)
+ return NULL;
+
+ return &m->creds;
+}
+
+_public_ int sd_bus_message_is_signal(sd_bus_message *m,
+ const char *interface,
+ const char *member) {
+ assert_return(m, -EINVAL);
+
+ if (m->header->type != SD_BUS_MESSAGE_SIGNAL)
+ return 0;
+
+ if (interface && (!m->interface || !streq(m->interface, interface)))
+ return 0;
+
+ if (member && (!m->member || !streq(m->member, member)))
+ return 0;
+
+ return 1;
+}
+
+_public_ int sd_bus_message_is_method_call(sd_bus_message *m,
+ const char *interface,
+ const char *member) {
+ assert_return(m, -EINVAL);
+
+ if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
+ return 0;
+
+ if (interface && (!m->interface || !streq(m->interface, interface)))
+ return 0;
+
+ if (member && (!m->member || !streq(m->member, member)))
+ return 0;
+
+ return 1;
+}
+
+_public_ int sd_bus_message_is_method_error(sd_bus_message *m, const char *name) {
+ assert_return(m, -EINVAL);
+
+ if (m->header->type != SD_BUS_MESSAGE_METHOD_ERROR)
+ return 0;
+
+ if (name && (!m->error.name || !streq(m->error.name, name)))
+ return 0;
+
+ return 1;
+}
+
+_public_ int sd_bus_message_set_no_reply(sd_bus_message *m, int b) {
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EPERM);
+
+ if (b)
+ m->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
+ else
+ m->header->flags &= ~BUS_MESSAGE_NO_REPLY_EXPECTED;
+
+ return 0;
+}
+
+_public_ int sd_bus_message_set_no_auto_start(sd_bus_message *m, int b) {
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+
+ if (b)
+ m->header->flags |= BUS_MESSAGE_NO_AUTO_START;
+ else
+ m->header->flags &= ~BUS_MESSAGE_NO_AUTO_START;
+
+ return 0;
+}
+
+static struct bus_container *message_get_container(sd_bus_message *m) {
+ assert(m);
+
+ if (m->n_containers == 0)
+ return &m->root_container;
+
+ assert(m->containers);
+ return m->containers + m->n_containers - 1;
+}
+
+struct bus_body_part *message_append_part(sd_bus_message *m) {
+ struct bus_body_part *part;
+
+ assert(m);
+
+ if (m->poisoned)
+ return NULL;
+
+ if (m->n_body_parts <= 0) {
+ part = &m->body;
+ zero(*part);
+ } else {
+ assert(m->body_end);
+
+ part = new0(struct bus_body_part, 1);
+ if (!part) {
+ m->poisoned = true;
+ return NULL;
+ }
+
+ m->body_end->next = part;
+ }
+
+ part->memfd = -1;
+ m->body_end = part;
+ m->n_body_parts ++;
+
+ return part;
+}
+
+static void part_zero(struct bus_body_part *part, size_t sz) {
+ assert(part);
+ assert(sz > 0);
+ assert(sz < 8);
+
+ /* All other fields can be left in their defaults */
+ assert(!part->data);
+ assert(part->memfd < 0);
+
+ part->size = sz;
+ part->is_zero = true;
+ part->sealed = true;
+}
+
+static int part_make_space(
+ struct sd_bus_message *m,
+ struct bus_body_part *part,
+ size_t sz,
+ void **q) {
+
+ void *n;
+ int r;
+
+ assert(m);
+ assert(part);
+ assert(!part->sealed);
+
+ if (m->poisoned)
+ return -ENOMEM;
+
+ if (!part->data && part->memfd < 0)
+ part->memfd = bus_kernel_pop_memfd(m->bus, &part->data, &part->mapped, &part->allocated);
+
+ if (part->memfd >= 0) {
+
+ if (part->allocated == 0 || sz > part->allocated) {
+ uint64_t new_allocated;
+
+ new_allocated = PAGE_ALIGN(sz > 0 ? 2 * sz : 1);
+ r = ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &new_allocated);
+ if (r < 0) {
+ m->poisoned = true;
+ return -errno;
+ }
+
+ part->allocated = new_allocated;
+ }
+
+ if (!part->data || sz > part->mapped) {
+ size_t psz;
+
+ psz = PAGE_ALIGN(sz > 0 ? sz : 1);
+ if (part->mapped <= 0)
+ n = mmap(NULL, psz, PROT_READ|PROT_WRITE, MAP_SHARED, part->memfd, 0);
+ else
+ n = mremap(part->data, part->mapped, psz, MREMAP_MAYMOVE);
+
+ if (n == MAP_FAILED) {
+ m->poisoned = true;
+ return -errno;
+ }
+
+ part->mapped = psz;
+ part->data = n;
+ }
+
+ part->munmap_this = true;
+ } else {
+ if (part->allocated == 0 || sz > part->allocated) {
+ size_t new_allocated;
+
+ new_allocated = sz > 0 ? 2 * sz : 64;
+ n = realloc(part->data, new_allocated);
+ if (!n) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+
+ part->data = n;
+ part->allocated = new_allocated;
+ part->free_this = true;
+ }
+ }
+
+ if (q)
+ *q = part->data ? (uint8_t*) part->data + part->size : NULL;
+
+ part->size = sz;
+ return 0;
+}
+
+static int message_add_offset(sd_bus_message *m, size_t offset) {
+ struct bus_container *c;
+
+ assert(m);
+ assert(BUS_MESSAGE_IS_GVARIANT(m));
+
+ /* Add offset to current container, unless this is the first
+ * item in it, which will have the 0 offset, which we can
+ * ignore. */
+ c = message_get_container(m);
+
+ if (!c->need_offsets)
+ return 0;
+
+ if (!GREEDY_REALLOC(c->offsets, c->offsets_allocated, c->n_offsets + 1))
+ return -ENOMEM;
+
+ c->offsets[c->n_offsets++] = offset;
+ return 0;
+}
+
+static void message_extend_containers(sd_bus_message *m, size_t expand) {
+ struct bus_container *c;
+
+ assert(m);
+
+ if (expand <= 0)
+ return;
+
+ /* Update counters */
+ for (c = m->containers; c < m->containers + m->n_containers; c++) {
+
+ if (c->array_size)
+ *c->array_size += expand;
+ }
+}
+
+static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz, bool add_offset) {
+ size_t start_body, end_body, padding, added;
+ void *p;
+ int r;
+
+ assert(m);
+ assert(align > 0);
+ assert(!m->sealed);
+
+ if (m->poisoned)
+ return NULL;
+
+ start_body = ALIGN_TO((size_t) m->header->body_size, align);
+ end_body = start_body + sz;
+
+ padding = start_body - m->header->body_size;
+ added = padding + sz;
+
+ /* Check for 32bit overflows */
+ if (end_body > (size_t) ((uint32_t) -1)) {
+ m->poisoned = true;
+ return NULL;
+ }
+
+ if (added > 0) {
+ struct bus_body_part *part = NULL;
+ bool add_new_part;
+
+ add_new_part =
+ m->n_body_parts <= 0 ||
+ m->body_end->sealed ||
+ padding != ALIGN_TO(m->body_end->size, align) - m->body_end->size;
+
+ if (add_new_part) {
+ if (padding > 0) {
+ part = message_append_part(m);
+ if (!part)
+ return NULL;
+
+ part_zero(part, padding);
+ }
+
+ part = message_append_part(m);
+ if (!part)
+ return NULL;
+
+ r = part_make_space(m, part, sz, &p);
+ if (r < 0)
+ return NULL;
+ } else {
+ struct bus_container *c;
+ void *op;
+ size_t os, start_part, end_part;
+
+ part = m->body_end;
+ op = part->data;
+ os = part->size;
+
+ start_part = ALIGN_TO(part->size, align);
+ end_part = start_part + sz;
+
+ r = part_make_space(m, part, end_part, &p);
+ if (r < 0)
+ return NULL;
+
+ if (padding > 0) {
+ memset(p, 0, padding);
+ p = (uint8_t*) p + padding;
+ }
+
+ /* Readjust pointers */
+ for (c = m->containers; c < m->containers + m->n_containers; c++)
+ c->array_size = adjust_pointer(c->array_size, op, os, part->data);
+
+ m->error.message = (const char*) adjust_pointer(m->error.message, op, os, part->data);
+ }
+ } else
+ /* Return something that is not NULL and is aligned */
+ p = (uint8_t *) NULL + align;
+
+ m->header->body_size = end_body;
+ message_extend_containers(m, added);
+
+ if (add_offset) {
+ r = message_add_offset(m, end_body);
+ if (r < 0) {
+ m->poisoned = true;
+ return NULL;
+ }
+ }
+
+ return p;
+}
+
+static int message_push_fd(sd_bus_message *m, int fd) {
+ int *f, copy;
+
+ assert(m);
+
+ if (fd < 0)
+ return -EINVAL;
+
+ if (!m->allow_fds)
+ return -ENOTSUP;
+
+ copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (copy < 0)
+ return -errno;
+
+ f = realloc(m->fds, sizeof(int) * (m->n_fds + 1));
+ if (!f) {
+ m->poisoned = true;
+ close_nointr_nofail(copy);
+ return -ENOMEM;
+ }
+
+ m->fds = f;
+ m->fds[m->n_fds] = copy;
+ m->free_fds = true;
+
+ return copy;
+}
+
+int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored) {
+ _cleanup_close_ int fd = -1;
+ struct bus_container *c;
+ ssize_t align, sz;
+ void *a;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(bus_type_is_basic(type), -EINVAL);
+ assert_return(!m->poisoned, -ESTALE);
+
+ c = message_get_container(m);
+
+ if (c->signature && c->signature[c->index]) {
+ /* Container signature is already set */
+
+ if (c->signature[c->index] != type)
+ return -ENXIO;
+ } else {
+ char *e;
+
+ /* Maybe we can append to the signature? But only if this is the top-level container*/
+ if (c->enclosing != 0)
+ return -ENXIO;
+
+ e = strextend(&c->signature, CHAR_TO_STR(type), NULL);
+ if (!e) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+ }
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ uint8_t u8;
+ uint32_t u32;
+
+ switch (type) {
+
+ case SD_BUS_TYPE_SIGNATURE:
+ case SD_BUS_TYPE_STRING:
+ p = strempty(p);
+
+ /* Fall through... */
+ case SD_BUS_TYPE_OBJECT_PATH:
+ if (!p)
+ return -EINVAL;
+
+ align = 1;
+ sz = strlen(p) + 1;
+ break;
+
+ case SD_BUS_TYPE_BOOLEAN:
+
+ u8 = p && *(int*) p;
+ p = &u8;
+
+ align = sz = 1;
+ break;
+
+ case SD_BUS_TYPE_UNIX_FD:
+
+ if (!p)
+ return -EINVAL;
+
+ fd = message_push_fd(m, *(int*) p);
+ if (fd < 0)
+ return fd;
+
+ u32 = m->n_fds;
+ p = &u32;
+
+ align = sz = 4;
+ break;
+
+ default:
+ align = bus_gvariant_get_alignment(CHAR_TO_STR(type));
+ sz = bus_gvariant_get_size(CHAR_TO_STR(type));
+ break;
+ }
+
+ assert(align > 0);
+ assert(sz > 0);
+
+ a = message_extend_body(m, align, sz, true);
+ if (!a)
+ return -ENOMEM;
+
+ memcpy(a, p, sz);
+
+ if (stored)
+ *stored = (const uint8_t*) a;
+
+ } else {
+ uint32_t u32;
+
+ switch (type) {
+
+ case SD_BUS_TYPE_STRING:
+ /* To make things easy we'll serialize a NULL string
+ * into the empty string */
+ p = strempty(p);
+
+ /* Fall through... */
+ case SD_BUS_TYPE_OBJECT_PATH:
+
+ if (!p)
+ return -EINVAL;
+
+ align = 4;
+ sz = 4 + strlen(p) + 1;
+ break;
+
+ case SD_BUS_TYPE_SIGNATURE:
+
+ p = strempty(p);
+
+ align = 1;
+ sz = 1 + strlen(p) + 1;
+ break;
+
+ case SD_BUS_TYPE_BOOLEAN:
+
+ u32 = p && *(int*) p;
+ p = &u32;
+
+ align = sz = 4;
+ break;
+
+ case SD_BUS_TYPE_UNIX_FD:
+
+ if (!p)
+ return -EINVAL;
+
+ fd = message_push_fd(m, *(int*) p);
+ if (fd < 0)
+ return fd;
+
+ u32 = m->n_fds;
+ p = &u32;
+
+ align = sz = 4;
+ break;
+
+ default:
+ align = bus_type_get_alignment(type);
+ sz = bus_type_get_size(type);
+ break;
+ }
+
+ assert(align > 0);
+ assert(sz > 0);
+
+ a = message_extend_body(m, align, sz, false);
+ if (!a)
+ return -ENOMEM;
+
+ if (type == SD_BUS_TYPE_STRING || type == SD_BUS_TYPE_OBJECT_PATH) {
+ *(uint32_t*) a = sz - 5;
+ memcpy((uint8_t*) a + 4, p, sz - 4);
+
+ if (stored)
+ *stored = (const uint8_t*) a + 4;
+
+ } else if (type == SD_BUS_TYPE_SIGNATURE) {
+ *(uint8_t*) a = sz - 2;
+ memcpy((uint8_t*) a + 1, p, sz - 1);
+
+ if (stored)
+ *stored = (const uint8_t*) a + 1;
+ } else {
+ memcpy(a, p, sz);
+
+ if (stored)
+ *stored = a;
+ }
+ }
+
+ if (type == SD_BUS_TYPE_UNIX_FD)
+ m->n_fds ++;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index++;
+
+ fd = -1;
+ return 0;
+}
+
+_public_ int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p) {
+ return message_append_basic(m, type, p, NULL);
+}
+
+_public_ int sd_bus_message_append_string_space(
+ sd_bus_message *m,
+ size_t size,
+ char **s) {
+
+ struct bus_container *c;
+ void *a;
+
+ assert_return(m, -EINVAL);
+ assert_return(s, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(!m->poisoned, -ESTALE);
+
+ c = message_get_container(m);
+
+ if (c->signature && c->signature[c->index]) {
+ /* Container signature is already set */
+
+ if (c->signature[c->index] != SD_BUS_TYPE_STRING)
+ return -ENXIO;
+ } else {
+ char *e;
+
+ /* Maybe we can append to the signature? But only if this is the top-level container*/
+ if (c->enclosing != 0)
+ return -ENXIO;
+
+ e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRING), NULL);
+ if (!e) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+ }
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ a = message_extend_body(m, 1, size + 1, true);
+ if (!a)
+ return -ENOMEM;
+
+ *s = a;
+ } else {
+ a = message_extend_body(m, 4, 4 + size + 1, false);
+ if (!a)
+ return -ENOMEM;
+
+ *(uint32_t*) a = size;
+ *s = (char*) a + 4;
+ }
+
+ (*s)[size] = 0;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index++;
+
+ return 0;
+}
+
+_public_ int sd_bus_message_append_string_iovec(
+ sd_bus_message *m,
+ const struct iovec *iov,
+ unsigned n) {
+
+ size_t size;
+ unsigned i;
+ char *p;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(iov || n == 0, -EINVAL);
+ assert_return(!m->poisoned, -ESTALE);
+
+ size = IOVEC_TOTAL_SIZE(iov, n);
+
+ r = sd_bus_message_append_string_space(m, size, &p);
+ if (r < 0)
+ return r;
+
+ for (i = 0; i < n; i++) {
+
+ if (iov[i].iov_base)
+ memcpy(p, iov[i].iov_base, iov[i].iov_len);
+ else
+ memset(p, ' ', iov[i].iov_len);
+
+ p += iov[i].iov_len;
+ }
+
+ return 0;
+}
+
+static int bus_message_open_array(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents,
+ uint32_t **array_size,
+ size_t *begin,
+ bool *need_offsets) {
+
+ unsigned nindex;
+ int alignment, r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+ assert(array_size);
+ assert(begin);
+ assert(need_offsets);
+
+ if (!signature_is_single(contents, true))
+ return -EINVAL;
+
+ if (c->signature && c->signature[c->index]) {
+
+ /* Verify the existing signature */
+
+ if (c->signature[c->index] != SD_BUS_TYPE_ARRAY)
+ return -ENXIO;
+
+ if (!startswith(c->signature + c->index + 1, contents))
+ return -ENXIO;
+
+ nindex = c->index + 1 + strlen(contents);
+ } else {
+ char *e;
+
+ if (c->enclosing != 0)
+ return -ENXIO;
+
+ /* Extend the existing signature */
+
+ e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_ARRAY), contents, NULL);
+ if (!e) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+
+ nindex = e - c->signature;
+ }
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ alignment = bus_gvariant_get_alignment(contents);
+ if (alignment < 0)
+ return alignment;
+
+ /* Add alignment padding and add to offset list */
+ if (!message_extend_body(m, alignment, 0, false))
+ return -ENOMEM;
+
+ r = bus_gvariant_is_fixed_size(contents);
+ if (r < 0)
+ return r;
+
+ *begin = m->header->body_size;
+ *need_offsets = r == 0;
+ } else {
+ void *a, *op;
+ size_t os;
+ struct bus_body_part *o;
+
+ alignment = bus_type_get_alignment(contents[0]);
+ if (alignment < 0)
+ return alignment;
+
+ a = message_extend_body(m, 4, 4, false);
+ if (!a)
+ return -ENOMEM;
+
+ o = m->body_end;
+ op = m->body_end->data;
+ os = m->body_end->size;
+
+ /* Add alignment between size and first element */
+ if (!message_extend_body(m, alignment, 0, false))
+ return -ENOMEM;
+
+ /* location of array size might have changed so let's readjust a */
+ if (o == m->body_end)
+ a = adjust_pointer(a, op, os, m->body_end->data);
+
+ *(uint32_t*) a = 0;
+ *array_size = a;
+ }
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index = nindex;
+
+ return 0;
+}
+
+static int bus_message_open_variant(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents) {
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_single(contents, false))
+ return -EINVAL;
+
+ if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN)
+ return -EINVAL;
+
+ if (c->signature && c->signature[c->index]) {
+
+ if (c->signature[c->index] != SD_BUS_TYPE_VARIANT)
+ return -ENXIO;
+
+ } else {
+ char *e;
+
+ if (c->enclosing != 0)
+ return -ENXIO;
+
+ e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_VARIANT), NULL);
+ if (!e) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+ }
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ /* Variants are always aligned to 8 */
+
+ if (!message_extend_body(m, 8, 0, false))
+ return -ENOMEM;
+
+ } else {
+ size_t l;
+ void *a;
+
+ l = strlen(contents);
+ a = message_extend_body(m, 1, 1 + l + 1, false);
+ if (!a)
+ return -ENOMEM;
+
+ *(uint8_t*) a = l;
+ memcpy((uint8_t*) a + 1, contents, l + 1);
+ }
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index++;
+
+ return 0;
+}
+
+static int bus_message_open_struct(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents,
+ size_t *begin,
+ bool *need_offsets) {
+
+ size_t nindex;
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+ assert(begin);
+ assert(need_offsets);
+
+ if (!signature_is_valid(contents, false))
+ return -EINVAL;
+
+ if (c->signature && c->signature[c->index]) {
+ size_t l;
+
+ l = strlen(contents);
+
+ if (c->signature[c->index] != SD_BUS_TYPE_STRUCT_BEGIN ||
+ !startswith(c->signature + c->index + 1, contents) ||
+ c->signature[c->index + 1 + l] != SD_BUS_TYPE_STRUCT_END)
+ return -ENXIO;
+
+ nindex = c->index + 1 + l + 1;
+ } else {
+ char *e;
+
+ if (c->enclosing != 0)
+ return -ENXIO;
+
+ e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_BEGIN), contents, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_END), NULL);
+ if (!e) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+
+ nindex = e - c->signature;
+ }
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ int alignment;
+
+ alignment = bus_gvariant_get_alignment(contents);
+ if (alignment < 0)
+ return alignment;
+
+ if (!message_extend_body(m, alignment, 0, false))
+ return -ENOMEM;
+
+ r = bus_gvariant_is_fixed_size(contents);
+ if (r < 0)
+ return r;
+
+ *begin = m->header->body_size;
+ *need_offsets = r == 0;
+ } else {
+ /* Align contents to 8 byte boundary */
+ if (!message_extend_body(m, 8, 0, false))
+ return -ENOMEM;
+ }
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index = nindex;
+
+ return 0;
+}
+
+static int bus_message_open_dict_entry(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents,
+ size_t *begin,
+ bool *need_offsets) {
+
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+ assert(begin);
+ assert(need_offsets);
+
+ if (!signature_is_pair(contents))
+ return -EINVAL;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ return -ENXIO;
+
+ if (c->signature && c->signature[c->index]) {
+ size_t l;
+
+ l = strlen(contents);
+
+ if (c->signature[c->index] != SD_BUS_TYPE_DICT_ENTRY_BEGIN ||
+ !startswith(c->signature + c->index + 1, contents) ||
+ c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END)
+ return -ENXIO;
+ } else
+ return -ENXIO;
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ int alignment;
+
+ alignment = bus_gvariant_get_alignment(contents);
+ if (alignment < 0)
+ return alignment;
+
+ if (!message_extend_body(m, alignment, 0, false))
+ return -ENOMEM;
+
+ r = bus_gvariant_is_fixed_size(contents);
+ if (r < 0)
+ return r;
+
+ *begin = m->header->body_size;
+ *need_offsets = r == 0;
+ } else {
+ /* Align contents to 8 byte boundary */
+ if (!message_extend_body(m, 8, 0, false))
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+_public_ int sd_bus_message_open_container(
+ sd_bus_message *m,
+ char type,
+ const char *contents) {
+
+ struct bus_container *c, *w;
+ uint32_t *array_size = NULL;
+ char *signature;
+ size_t before, begin;
+ bool need_offsets = false;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(contents, -EINVAL);
+ assert_return(!m->poisoned, -ESTALE);
+
+ /* Make sure we have space for one more container */
+ if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1)) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+
+ c = message_get_container(m);
+
+ signature = strdup(contents);
+ if (!signature) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+
+ /* Save old index in the parent container, in case we have to
+ * abort this container */
+ c->saved_index = c->index;
+ before = m->header->body_size;
+
+ if (type == SD_BUS_TYPE_ARRAY)
+ r = bus_message_open_array(m, c, contents, &array_size, &begin, &need_offsets);
+ else if (type == SD_BUS_TYPE_VARIANT)
+ r = bus_message_open_variant(m, c, contents);
+ else if (type == SD_BUS_TYPE_STRUCT)
+ r = bus_message_open_struct(m, c, contents, &begin, &need_offsets);
+ else if (type == SD_BUS_TYPE_DICT_ENTRY)
+ r = bus_message_open_dict_entry(m, c, contents, &begin, &need_offsets);
+ else
+ r = -EINVAL;
+
+ if (r < 0) {
+ free(signature);
+ return r;
+ }
+
+ /* OK, let's fill it in */
+ w = m->containers + m->n_containers++;
+ w->enclosing = type;
+ w->signature = signature;
+ w->index = 0;
+ w->array_size = array_size;
+ w->before = before;
+ w->begin = begin;
+ w->n_offsets = w->offsets_allocated = 0;
+ w->offsets = NULL;
+ w->need_offsets = need_offsets;
+
+ return 0;
+}
+
+static size_t determine_word_size(size_t sz, size_t extra) {
+ if (sz + extra <= 0xFF)
+ return 1;
+ else if (sz + extra*2 <= 0xFFFF)
+ return 2;
+ else if (sz + extra*4 <= 0xFFFFFFFF)
+ return 4;
+ else
+ return 8;
+}
+
+static size_t read_word_le(void *p, size_t sz) {
+ union {
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ } x;
+
+ assert(p);
+
+ if (sz == 1)
+ return *(uint8_t*) p;
+
+ memcpy(&x, p, sz);
+
+ if (sz == 2)
+ return le16toh(x.u16);
+ else if (sz == 4)
+ return le32toh(x.u32);
+ else if (sz == 8)
+ return le64toh(x.u64);
+
+ assert_not_reached("unknown word width");
+}
+
+static void write_word_le(void *p, size_t sz, size_t value) {
+ union {
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ } x;
+
+ assert(p);
+ assert(sz == 8 || (value < (1ULL << (sz*8))));
+
+ if (sz == 1) {
+ *(uint8_t*) p = value;
+ return;
+ } else if (sz == 2)
+ x.u16 = htole16((uint16_t) value);
+ else if (sz == 4)
+ x.u32 = htole32((uint32_t) value);
+ else if (sz == 8)
+ x.u64 = htole64((uint64_t) value);
+ else
+ assert_not_reached("unknown word width");
+
+ memcpy(p, &x, sz);
+}
+
+static int bus_message_close_array(sd_bus_message *m, struct bus_container *c) {
+
+ assert(m);
+ assert(c);
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m))
+ return 0;
+
+ if (c->need_offsets) {
+ size_t payload, sz, i;
+ uint8_t *a;
+
+ /* Variable-width arrays */
+
+ payload = c->n_offsets > 0 ? c->offsets[c->n_offsets-1] - c->begin : 0;
+ sz = determine_word_size(payload, c->n_offsets);
+
+ a = message_extend_body(m, 1, sz * c->n_offsets, true);
+ if (!a)
+ return -ENOMEM;
+
+ for (i = 0; i < c->n_offsets; i++)
+ write_word_le(a + sz*i, sz, c->offsets[i] - c->begin);
+ } else {
+ void *a;
+
+ /* Fixed-width or empty arrays */
+
+ a = message_extend_body(m, 1, 0, true); /* let's add offset to parent */
+ if (!a)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int bus_message_close_variant(sd_bus_message *m, struct bus_container *c) {
+ uint8_t *a;
+ size_t l;
+
+ assert(m);
+ assert(c);
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m))
+ return 0;
+
+ l = strlen(c->signature);
+
+ a = message_extend_body(m, 1, 1 + l, true);
+ if (!a)
+ return -ENOMEM;
+
+ a[0] = 0;
+ memcpy(a+1, c->signature, l);
+
+ return 0;
+}
+
+static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c, bool add_offset) {
+ size_t n_variable = 0;
+ unsigned i = 0;
+ const char *p;
+ uint8_t *a;
+ int r;
+
+ assert(m);
+ assert(c);
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m))
+ return 0;
+
+ p = strempty(c->signature);
+ while (*p != 0) {
+ size_t n;
+
+ r = signature_element_length(p, &n);
+ if (r < 0)
+ return r;
+ else {
+ char t[n+1];
+
+ memcpy(t, p, n);
+ t[n] = 0;
+
+ r = bus_gvariant_is_fixed_size(t);
+ if (r < 0)
+ return r;
+ }
+
+ assert(!c->need_offsets || i <= c->n_offsets);
+
+ /* We need to add an offset for each item that has a
+ * variable size and that is not the last one in the
+ * list */
+ if (r == 0 && p[n] != 0)
+ n_variable++;
+
+ i++;
+ p += n;
+ }
+
+ assert(!c->need_offsets || i == c->n_offsets);
+ assert(c->need_offsets || n_variable == 0);
+
+ if (n_variable <= 0) {
+ a = message_extend_body(m, 1, 0, add_offset);
+ if (!a)
+ return -ENOMEM;
+ } else {
+ size_t sz;
+ unsigned j;
+
+ assert(c->offsets[c->n_offsets-1] == m->header->body_size);
+
+ sz = determine_word_size(m->header->body_size - c->begin, n_variable);
+
+ a = message_extend_body(m, 1, sz * n_variable, add_offset);
+ if (!a)
+ return -ENOMEM;
+
+ p = strempty(c->signature);
+ for (i = 0, j = 0; i < c->n_offsets; i++) {
+ unsigned k;
+ size_t n;
+
+ r = signature_element_length(p, &n);
+ if (r < 0)
+ return r;
+ else {
+ char t[n+1];
+
+ memcpy(t, p, n);
+ t[n] = 0;
+
+ p += n;
+
+ r = bus_gvariant_is_fixed_size(t);
+ if (r < 0)
+ return r;
+ if (r > 0 || p[0] == 0)
+ continue;
+ }
+
+ k = n_variable - 1 - j;
+
+ write_word_le(a + k * sz, sz, c->offsets[i] - c->begin);
+
+ j++;
+ }
+ }
+
+ return 0;
+}
+
+_public_ int sd_bus_message_close_container(sd_bus_message *m) {
+ struct bus_container *c;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(m->n_containers > 0, -EINVAL);
+ assert_return(!m->poisoned, -ESTALE);
+
+ c = message_get_container(m);
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ if (c->signature && c->signature[c->index] != 0)
+ return -EINVAL;
+
+ m->n_containers--;
+
+ if (c->enclosing == SD_BUS_TYPE_ARRAY)
+ r = bus_message_close_array(m, c);
+ else if (c->enclosing == SD_BUS_TYPE_VARIANT)
+ r = bus_message_close_variant(m, c);
+ else if (c->enclosing == SD_BUS_TYPE_STRUCT || c->enclosing == SD_BUS_TYPE_DICT_ENTRY)
+ r = bus_message_close_struct(m, c, true);
+ else
+ assert_not_reached("Unknown container type");
+
+ free(c->signature);
+ free(c->offsets);
+
+ return r;
+}
+
+typedef struct {
+ const char *types;
+ unsigned n_struct;
+ unsigned n_array;
+} TypeStack;
+
+static int type_stack_push(TypeStack *stack, unsigned max, unsigned *i, const char *types, unsigned n_struct, unsigned n_array) {
+ assert(stack);
+ assert(max > 0);
+
+ if (*i >= max)
+ return -EINVAL;
+
+ stack[*i].types = types;
+ stack[*i].n_struct = n_struct;
+ stack[*i].n_array = n_array;
+ (*i)++;
+
+ return 0;
+}
+
+static int type_stack_pop(TypeStack *stack, unsigned max, unsigned *i, const char **types, unsigned *n_struct, unsigned *n_array) {
+ assert(stack);
+ assert(max > 0);
+ assert(types);
+ assert(n_struct);
+ assert(n_array);
+
+ if (*i <= 0)
+ return 0;
+
+ (*i)--;
+ *types = stack[*i].types;
+ *n_struct = stack[*i].n_struct;
+ *n_array = stack[*i].n_array;
+
+ return 1;
+}
+
+int bus_message_append_ap(
+ sd_bus_message *m,
+ const char *types,
+ va_list ap) {
+
+ unsigned n_array, n_struct;
+ TypeStack stack[BUS_CONTAINER_DEPTH];
+ unsigned stack_ptr = 0;
+ int r;
+
+ assert(m);
+
+ if (!types)
+ return 0;
+
+ n_array = (unsigned) -1;
+ n_struct = strlen(types);
+
+ for (;;) {
+ const char *t;
+
+ if (n_array == 0 || (n_array == (unsigned) -1 && n_struct == 0)) {
+ r = type_stack_pop(stack, ELEMENTSOF(stack), &stack_ptr, &types, &n_struct, &n_array);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ continue;
+ }
+
+ t = types;
+ if (n_array != (unsigned) -1)
+ n_array --;
+ else {
+ types ++;
+ n_struct--;
+ }
+
+ switch (*t) {
+
+ case SD_BUS_TYPE_BYTE: {
+ uint8_t x;
+
+ x = (uint8_t) va_arg(ap, int);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_UNIX_FD: {
+ uint32_t x;
+
+ /* We assume a boolean is the same as int32_t */
+ assert_cc(sizeof(int32_t) == sizeof(int));
+
+ x = va_arg(ap, uint32_t);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16: {
+ uint16_t x;
+
+ x = (uint16_t) va_arg(ap, int);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE: {
+ uint64_t x;
+
+ x = va_arg(ap, uint64_t);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE: {
+ const char *x;
+
+ x = va_arg(ap, const char*);
+ r = sd_bus_message_append_basic(m, *t, x);
+ break;
+ }
+
+ case SD_BUS_TYPE_ARRAY: {
+ size_t k;
+
+ r = signature_element_length(t + 1, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k + 1];
+ memcpy(s, t + 1, k);
+ s[k] = 0;
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
+ if (r < 0)
+ return r;
+ }
+
+ if (n_array == (unsigned) -1) {
+ types += k;
+ n_struct -= k;
+ }
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = t + 1;
+ n_struct = k;
+ n_array = va_arg(ap, unsigned);
+
+ break;
+ }
+
+ case SD_BUS_TYPE_VARIANT: {
+ const char *s;
+
+ s = va_arg(ap, const char*);
+ if (!s)
+ return -EINVAL;
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, s);
+ if (r < 0)
+ return r;
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = s;
+ n_struct = strlen(s);
+ n_array = (unsigned) -1;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
+ size_t k;
+
+ r = signature_element_length(t, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k - 1];
+
+ memcpy(s, t + 1, k - 2);
+ s[k - 2] = 0;
+
+ r = sd_bus_message_open_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
+ if (r < 0)
+ return r;
+ }
+
+ if (n_array == (unsigned) -1) {
+ types += k - 1;
+ n_struct -= k - 1;
+ }
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = t + 1;
+ n_struct = k - 2;
+ n_array = (unsigned) -1;
+
+ break;
+ }
+
+ default:
+ r = -EINVAL;
+ }
+
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
+
+_public_ int sd_bus_message_append(sd_bus_message *m, const char *types, ...) {
+ va_list ap;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(types, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(!m->poisoned, -ESTALE);
+
+ va_start(ap, types);
+ r = bus_message_append_ap(m, types, ap);
+ va_end(ap);
+
+ return r;
+}
+
+_public_ int sd_bus_message_append_array_space(
+ sd_bus_message *m,
+ char type,
+ size_t size,
+ void **ptr) {
+
+ ssize_t align, sz;
+ void *a;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(bus_type_is_trivial(type) && type != SD_BUS_TYPE_BOOLEAN, -EINVAL);
+ assert_return(ptr || size == 0, -EINVAL);
+ assert_return(!m->poisoned, -ESTALE);
+
+ /* alignment and size of the trivial types (except bool) is
+ * identical for gvariant and dbus1 marshalling */
+ align = bus_type_get_alignment(type);
+ sz = bus_type_get_size(type);
+
+ assert_se(align > 0);
+ assert_se(sz > 0);
+
+ if (size % sz != 0)
+ return -EINVAL;
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type));
+ if (r < 0)
+ return r;
+
+ a = message_extend_body(m, align, size, false);
+ if (!a)
+ return -ENOMEM;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ *ptr = a;
+ return 0;
+}
+
+_public_ int sd_bus_message_append_array(sd_bus_message *m,
+ char type,
+ const void *ptr,
+ size_t size) {
+ int r;
+ void *p;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(bus_type_is_trivial(type), -EINVAL);
+ assert_return(ptr || size == 0, -EINVAL);
+ assert_return(!m->poisoned, -ESTALE);
+
+ r = sd_bus_message_append_array_space(m, type, size, &p);
+ if (r < 0)
+ return r;
+
+ if (size > 0)
+ memcpy(p, ptr, size);
+
+ return 0;
+}
+
+_public_ int sd_bus_message_append_array_iovec(
+ sd_bus_message *m,
+ char type,
+ const struct iovec *iov,
+ unsigned n) {
+
+ size_t size;
+ unsigned i;
+ void *p;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(bus_type_is_trivial(type), -EINVAL);
+ assert_return(iov || n == 0, -EINVAL);
+ assert_return(!m->poisoned, -ESTALE);
+
+ size = IOVEC_TOTAL_SIZE(iov, n);
+
+ r = sd_bus_message_append_array_space(m, type, size, &p);
+ if (r < 0)
+ return r;
+
+ for (i = 0; i < n; i++) {
+
+ if (iov[i].iov_base)
+ memcpy(p, iov[i].iov_base, iov[i].iov_len);
+ else
+ memset(p, 0, iov[i].iov_len);
+
+ p = (uint8_t*) p + iov[i].iov_len;
+ }
+
+ return 0;
+}
+
+_public_ int sd_bus_message_append_array_memfd(sd_bus_message *m,
+ char type,
+ sd_memfd *memfd) {
+ _cleanup_close_ int copy_fd = -1;
+ struct bus_body_part *part;
+ ssize_t align, sz;
+ uint64_t size;
+ void *a;
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (!memfd)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (!bus_type_is_trivial(type))
+ return -EINVAL;
+ if (m->poisoned)
+ return -ESTALE;
+
+ r = sd_memfd_set_sealed(memfd, true);
+ if (r < 0)
+ return r;
+
+ copy_fd = sd_memfd_dup_fd(memfd);
+ if (copy_fd < 0)
+ return copy_fd;
+
+ r = sd_memfd_get_size(memfd, &size);
+ if (r < 0)
+ return r;
+
+ align = bus_type_get_alignment(type);
+ sz = bus_type_get_size(type);
+
+ assert_se(align > 0);
+ assert_se(sz > 0);
+
+ if (size % sz != 0)
+ return -EINVAL;
+
+ if (size > (uint64_t) (uint32_t) -1)
+ return -EINVAL;
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type));
+ if (r < 0)
+ return r;
+
+ a = message_extend_body(m, align, 0, false);
+ if (!a)
+ return -ENOMEM;
+
+ part = message_append_part(m);
+ if (!part)
+ return -ENOMEM;
+
+ part->memfd = copy_fd;
+ part->sealed = true;
+ part->size = size;
+ copy_fd = -1;
+
+ m->header->body_size += size;
+ message_extend_containers(m, size);
+
+ return sd_bus_message_close_container(m);
+}
+
+_public_ int sd_bus_message_append_string_memfd(sd_bus_message *m, sd_memfd *memfd) {
+ _cleanup_close_ int copy_fd = -1;
+ struct bus_body_part *part;
+ struct bus_container *c;
+ uint64_t size;
+ void *a;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(memfd, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(!m->poisoned, -ESTALE);
+
+ r = sd_memfd_set_sealed(memfd, true);
+ if (r < 0)
+ return r;
+
+ copy_fd = sd_memfd_dup_fd(memfd);
+ if (copy_fd < 0)
+ return copy_fd;
+
+ r = sd_memfd_get_size(memfd, &size);
+ if (r < 0)
+ return r;
+
+ /* We require this to be NUL terminated */
+ if (size == 0)
+ return -EINVAL;
+
+ if (size > (uint64_t) (uint32_t) -1)
+ return -EINVAL;
+
+ c = message_get_container(m);
+ if (c->signature && c->signature[c->index]) {
+ /* Container signature is already set */
+
+ if (c->signature[c->index] != SD_BUS_TYPE_STRING)
+ return -ENXIO;
+ } else {
+ char *e;
+
+ /* Maybe we can append to the signature? But only if this is the top-level container*/
+ if (c->enclosing != 0)
+ return -ENXIO;
+
+ e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRING), NULL);
+ if (!e) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+ }
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m)) {
+ a = message_extend_body(m, 4, 4, false);
+ if (!a)
+ return -ENOMEM;
+
+ *(uint32_t*) a = size - 1;
+ }
+
+ part = message_append_part(m);
+ if (!part)
+ return -ENOMEM;
+
+ part->memfd = copy_fd;
+ part->sealed = true;
+ part->size = size;
+ copy_fd = -1;
+
+ m->header->body_size += size;
+ message_extend_containers(m, size);
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ r = message_add_offset(m, m->header->body_size);
+ if (r < 0) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+ }
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index++;
+
+ return 0;
+}
+
+_public_ int sd_bus_message_append_strv(sd_bus_message *m, char **l) {
+ char **i;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(!m->poisoned, -ESTALE);
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, l) {
+ r = sd_bus_message_append_basic(m, 's', *i);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(m);
+}
+
+static int bus_message_close_header(sd_bus_message *m) {
+ uint8_t *a;
+ size_t sz, i;
+
+ assert(m);
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m))
+ return 0;
+
+ if (m->n_header_offsets < 1)
+ return 0;
+
+ assert(m->header->fields_size == m->header_offsets[m->n_header_offsets-1]);
+
+ sz = determine_word_size(m->header->fields_size, m->n_header_offsets);
+
+ a = message_extend_fields(m, 1, sz * m->n_header_offsets, false);
+ if (!a)
+ return -ENOMEM;
+
+ for (i = 0; i < m->n_header_offsets; i++)
+ write_word_le(a + sz*i, sz, m->header_offsets[i]);
+
+ return 0;
+}
+
+int bus_message_seal(sd_bus_message *m, uint64_t cookie, usec_t timeout) {
+ struct bus_body_part *part;
+ size_t l, a;
+ unsigned i;
+ int r;
+
+ assert(m);
+
+ if (m->sealed)
+ return -EPERM;
+
+ if (m->n_containers > 0)
+ return -EBADMSG;
+
+ if (m->poisoned)
+ return -ESTALE;
+
+ /* In vtables the return signature of method calls is listed,
+ * let's check if they match if this is a response */
+ if (m->header->type == SD_BUS_MESSAGE_METHOD_RETURN &&
+ m->enforced_reply_signature &&
+ !streq(strempty(m->root_container.signature), m->enforced_reply_signature))
+ return -ENOMSG;
+
+ /* If gvariant marshalling is used we need to close the body structure */
+ r = bus_message_close_struct(m, &m->root_container, false);
+ if (r < 0)
+ return r;
+
+ /* If there's a non-trivial signature set, then add it in here */
+ if (!isempty(m->root_container.signature)) {
+ r = message_append_field_signature(m, BUS_MESSAGE_HEADER_SIGNATURE, m->root_container.signature, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ if (m->n_fds > 0) {
+ r = message_append_field_uint32(m, BUS_MESSAGE_HEADER_UNIX_FDS, m->n_fds);
+ if (r < 0)
+ return r;
+ }
+
+ r = bus_message_close_header(m);
+ if (r < 0)
+ return r;
+
+ m->header->serial = (uint32_t) cookie;
+ m->timeout = m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED ? 0 : timeout;
+
+ /* Add padding at the end of the fields part, since we know
+ * the body needs to start at an 8 byte alignment. We made
+ * sure we allocated enough space for this, so all we need to
+ * do here is to zero it out. */
+ l = BUS_MESSAGE_FIELDS_SIZE(m);
+ a = ALIGN8(l) - l;
+ if (a > 0)
+ memset((uint8_t*) BUS_MESSAGE_FIELDS(m) + l, 0, a);
+
+ /* If this is something we can send as memfd, then let's seal
+ the memfd now. Note that we can send memfds as payload only
+ for directed messages, and not for broadcasts. */
+ if (m->destination && m->bus && m->bus->use_memfd) {
+ MESSAGE_FOREACH_PART(part, i, m)
+ if (part->memfd >= 0 && !part->sealed && (part->size > MEMFD_MIN_SIZE || m->bus->use_memfd < 0)) {
+ uint64_t sz;
+
+ /* Try to seal it if that makes
+ * sense. First, unmap our own map to
+ * make sure we don't keep it busy. */
+ bus_body_part_unmap(part);
+
+ /* Then, sync up real memfd size */
+ sz = part->size;
+ if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &sz) < 0)
+ return -errno;
+
+ /* Finally, try to seal */
+ if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1) >= 0)
+ part->sealed = true;
+ }
+ }
+
+ m->root_container.end = BUS_MESSAGE_BODY_SIZE(m);
+ m->root_container.index = 0;
+ m->root_container.offset_index = 0;
+ m->root_container.item_size = m->root_container.n_offsets > 0 ? m->root_container.offsets[0] : 0;
+
+ m->sealed = true;
+
+ return 0;
+}
+
+int bus_body_part_map(struct bus_body_part *part) {
+ void *p;
+ size_t psz;
+
+ assert_se(part);
+
+ if (part->data)
+ return 0;
+
+ if (part->size <= 0)
+ return 0;
+
+ /* For smaller zero parts (as used for padding) we don't need to map anything... */
+ if (part->memfd < 0 && part->is_zero && part->size < 8) {
+ static const uint8_t zeroes[7] = { };
+ part->data = (void*) zeroes;
+ return 0;
+ }
+
+ psz = PAGE_ALIGN(part->size);
+
+ if (part->memfd >= 0)
+ p = mmap(NULL, psz, PROT_READ, MAP_SHARED, part->memfd, 0);
+ else if (part->is_zero)
+ p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ else
+ return -EINVAL;
+
+ if (p == MAP_FAILED)
+ return -errno;
+
+ part->mapped = psz;
+ part->data = p;
+ part->munmap_this = true;
+
+ return 0;
+}
+
+void bus_body_part_unmap(struct bus_body_part *part) {
+
+ assert_se(part);
+
+ if (part->memfd < 0)
+ return;
+
+ if (!part->data)
+ return;
+
+ if (!part->munmap_this)
+ return;
+
+ assert_se(munmap(part->data, part->mapped) == 0);
+
+ part->data = NULL;
+ part->mapped = 0;
+ part->munmap_this = false;
+
+ return;
+}
+
+static int buffer_peek(const void *p, uint32_t sz, size_t *rindex, size_t align, size_t nbytes, void **r) {
+ size_t k, start, end;
+
+ assert(rindex);
+ assert(align > 0);
+
+ start = ALIGN_TO((size_t) *rindex, align);
+ end = start + nbytes;
+
+ if (end > sz)
+ return -EBADMSG;
+
+ /* Verify that padding is 0 */
+ for (k = *rindex; k < start; k++)
+ if (((const uint8_t*) p)[k] != 0)
+ return -EBADMSG;
+
+ if (r)
+ *r = (uint8_t*) p + start;
+
+ *rindex = end;
+
+ return 1;
+}
+
+static bool message_end_of_signature(sd_bus_message *m) {
+ struct bus_container *c;
+
+ assert(m);
+
+ c = message_get_container(m);
+ return !c->signature || c->signature[c->index] == 0;
+}
+
+static bool message_end_of_array(sd_bus_message *m, size_t index) {
+ struct bus_container *c;
+
+ assert(m);
+
+ c = message_get_container(m);
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ return false;
+
+ if (BUS_MESSAGE_IS_GVARIANT(m))
+ return index >= c->end;
+ else {
+ assert(c->array_size);
+ return index >= c->begin + BUS_MESSAGE_BSWAP32(m, *c->array_size);
+ }
+}
+
+_public_ int sd_bus_message_at_end(sd_bus_message *m, int complete) {
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+
+ if (complete && m->n_containers > 0)
+ return false;
+
+ if (message_end_of_signature(m))
+ return true;
+
+ if (message_end_of_array(m, m->rindex))
+ return true;
+
+ return false;
+}
+
+static struct bus_body_part* find_part(sd_bus_message *m, size_t index, size_t sz, void **p) {
+ struct bus_body_part *part;
+ size_t begin;
+ int r;
+
+ assert(m);
+
+ if (m->cached_rindex_part && index >= m->cached_rindex_part_begin) {
+ part = m->cached_rindex_part;
+ begin = m->cached_rindex_part_begin;
+ } else {
+ part = &m->body;
+ begin = 0;
+ }
+
+ while (part) {
+ if (index < begin)
+ return NULL;
+
+ if (index + sz <= begin + part->size) {
+
+ r = bus_body_part_map(part);
+ if (r < 0)
+ return NULL;
+
+ if (p)
+ *p = (uint8_t*) part->data + index - begin;
+
+ m->cached_rindex_part = part;
+ m->cached_rindex_part_begin = begin;
+
+ return part;
+ }
+
+ begin += part->size;
+ part = part->next;
+ }
+
+ return NULL;
+}
+
+static int container_next_item(sd_bus_message *m, struct bus_container *c, size_t *rindex) {
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(rindex);
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m))
+ return 0;
+
+ if (c->enclosing == SD_BUS_TYPE_ARRAY) {
+ int sz;
+
+ sz = bus_gvariant_get_size(c->signature);
+ if (sz < 0) {
+ int alignment;
+
+ if (c->offset_index+1 >= c->n_offsets)
+ goto end;
+
+ /* Variable-size array */
+
+ alignment = bus_gvariant_get_alignment(c->signature);
+ assert(alignment > 0);
+
+ *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment);
+ c->item_size = c->offsets[c->offset_index+1] - *rindex;
+ } else {
+
+ if (c->offset_index+1 >= (c->end-c->begin)/sz)
+ goto end;
+
+ /* Fixed-size array */
+ *rindex = c->begin + (c->offset_index+1) * sz;
+ c->item_size = sz;
+ }
+
+ c->offset_index++;
+
+ } else if (c->enclosing == 0 ||
+ c->enclosing == SD_BUS_TYPE_STRUCT ||
+ c->enclosing == SD_BUS_TYPE_DICT_ENTRY) {
+
+ int alignment;
+ size_t n, j;
+
+ if (c->offset_index+1 >= c->n_offsets)
+ goto end;
+
+ r = signature_element_length(c->signature + c->index, &n);
+ if (r < 0)
+ return r;
+
+ r = signature_element_length(c->signature + c->index + n, &j);
+ if (r < 0)
+ return r;
+ else {
+ char t[j+1];
+ memcpy(t, c->signature + c->index + n, j);
+ t[j] = 0;
+
+ alignment = bus_gvariant_get_alignment(t);
+ }
+
+ assert(alignment > 0);
+
+ *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment);
+ c->item_size = c->offsets[c->offset_index+1] - *rindex;
+
+ c->offset_index++;
+
+ } else if (c->enclosing == SD_BUS_TYPE_VARIANT)
+ goto end;
+ else
+ assert_not_reached("Unknown container type");
+
+ return 0;
+
+end:
+ /* Reached the end */
+ *rindex = c->end;
+ c->item_size = 0;
+ return 0;
+}
+
+
+static int message_peek_body(
+ sd_bus_message *m,
+ size_t *rindex,
+ size_t align,
+ size_t nbytes,
+ void **ret) {
+
+ size_t k, start, end, padding;
+ struct bus_body_part *part;
+ uint8_t *q;
+
+ assert(m);
+ assert(rindex);
+ assert(align > 0);
+
+ start = ALIGN_TO((size_t) *rindex, align);
+ padding = start - *rindex;
+ end = start + nbytes;
+
+ if (end > BUS_MESSAGE_BODY_SIZE(m))
+ return -EBADMSG;
+
+ part = find_part(m, *rindex, padding, (void**) &q);
+ if (!part)
+ return -EBADMSG;
+
+ if (q) {
+ /* Verify padding */
+ for (k = 0; k < padding; k++)
+ if (q[k] != 0)
+ return -EBADMSG;
+ }
+
+ part = find_part(m, start, nbytes, (void**) &q);
+ if (!part || (nbytes > 0 && !q))
+ return -EBADMSG;
+
+ *rindex = end;
+
+ if (ret)
+ *ret = q;
+
+ return 0;
+}
+
+static bool validate_nul(const char *s, size_t l) {
+
+ /* Check for NUL chars in the string */
+ if (memchr(s, 0, l))
+ return false;
+
+ /* Check for NUL termination */
+ if (s[l] != 0)
+ return false;
+
+ return true;
+}
+
+static bool validate_string(const char *s, size_t l) {
+
+ if (!validate_nul(s, l))
+ return false;
+
+ /* Check if valid UTF8 */
+ if (!utf8_is_valid(s))
+ return false;
+
+ return true;
+}
+
+static bool validate_signature(const char *s, size_t l) {
+
+ if (!validate_nul(s, l))
+ return false;
+
+ /* Check if valid signature */
+ if (!signature_is_valid(s, true))
+ return false;
+
+ return true;
+}
+
+static bool validate_object_path(const char *s, size_t l) {
+
+ if (!validate_nul(s, l))
+ return false;
+
+ if (!object_path_is_valid(s))
+ return false;
+
+ return true;
+}
+
+_public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
+ struct bus_container *c;
+ size_t rindex;
+ void *q;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+ assert_return(bus_type_is_basic(type), -EINVAL);
+
+ if (message_end_of_signature(m))
+ return -ENXIO;
+
+ if (message_end_of_array(m, m->rindex))
+ return 0;
+
+ c = message_get_container(m);
+ if (c->signature[c->index] != type)
+ return -ENXIO;
+
+ rindex = m->rindex;
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+
+ if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) {
+ bool ok;
+
+ r = message_peek_body(m, &rindex, 1, c->item_size, &q);
+ if (r < 0)
+ return r;
+
+ if (type == SD_BUS_TYPE_STRING)
+ ok = validate_string(q, c->item_size-1);
+ else if (type == SD_BUS_TYPE_OBJECT_PATH)
+ ok = validate_object_path(q, c->item_size-1);
+ else
+ ok = validate_signature(q, c->item_size-1);
+
+ if (!ok)
+ return -EBADMSG;
+
+ if (p)
+ *(const char**) p = q;
+ } else {
+ int sz, align;
+
+ sz = bus_gvariant_get_size(CHAR_TO_STR(type));
+ assert(sz > 0);
+ if ((size_t) sz != c->item_size)
+ return -EBADMSG;
+
+ align = bus_gvariant_get_alignment(CHAR_TO_STR(type));
+ assert(align > 0);
+
+ r = message_peek_body(m, &rindex, align, c->item_size, &q);
+ if (r < 0)
+ return r;
+
+ switch (type) {
+
+ case SD_BUS_TYPE_BYTE:
+ if (p)
+ *(uint8_t*) p = *(uint8_t*) q;
+ break;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ if (p)
+ *(int*) p = !!*(uint8_t*) q;
+ break;
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ if (p)
+ *(uint16_t*) p = BUS_MESSAGE_BSWAP16(m, *(uint16_t*) q);
+ break;
+
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ if (p)
+ *(uint32_t*) p = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
+ break;
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ if (p)
+ *(uint64_t*) p = BUS_MESSAGE_BSWAP64(m, *(uint64_t*) q);
+ break;
+
+ case SD_BUS_TYPE_UNIX_FD: {
+ uint32_t j;
+
+ j = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
+ if (j >= m->n_fds)
+ return -EBADMSG;
+
+ if (p)
+ *(int*) p = m->fds[j];
+
+ break;
+ }
+
+ default:
+ assert_not_reached("unexpected type");
+ }
+ }
+
+ r = container_next_item(m, c, &rindex);
+ if (r < 0)
+ return r;
+ } else {
+
+ rindex = m->rindex;
+
+ if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH)) {
+ uint32_t l;
+ bool ok;
+
+ r = message_peek_body(m, &rindex, 4, 4, &q);
+ if (r < 0)
+ return r;
+
+ l = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
+ r = message_peek_body(m, &rindex, 1, l+1, &q);
+ if (r < 0)
+ return r;
+
+ if (type == SD_BUS_TYPE_OBJECT_PATH)
+ ok = validate_object_path(q, l);
+ else
+ ok = validate_string(q, l);
+ if (!ok)
+ return -EBADMSG;
+
+ if (p)
+ *(const char**) p = q;
+
+ } else if (type == SD_BUS_TYPE_SIGNATURE) {
+ uint8_t l;
+
+ r = message_peek_body(m, &rindex, 1, 1, &q);
+ if (r < 0)
+ return r;
+
+ l = *(uint8_t*) q;
+ r = message_peek_body(m, &rindex, 1, l+1, &q);
+ if (r < 0)
+ return r;
+
+ if (!validate_signature(q, l))
+ return -EBADMSG;
+
+ if (p)
+ *(const char**) p = q;
+
+ } else {
+ ssize_t sz, align;
+
+ align = bus_type_get_alignment(type);
+ assert(align > 0);
+
+ sz = bus_type_get_size(type);
+ assert(sz > 0);
+
+ r = message_peek_body(m, &rindex, align, sz, &q);
+ if (r < 0)
+ return r;
+
+ switch (type) {
+
+ case SD_BUS_TYPE_BYTE:
+ if (p)
+ *(uint8_t*) p = *(uint8_t*) q;
+ break;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ if (p)
+ *(int*) p = !!*(uint32_t*) q;
+ break;
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ if (p)
+ *(uint16_t*) p = BUS_MESSAGE_BSWAP16(m, *(uint16_t*) q);
+ break;
+
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ if (p)
+ *(uint32_t*) p = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
+ break;
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ if (p)
+ *(uint64_t*) p = BUS_MESSAGE_BSWAP64(m, *(uint64_t*) q);
+ break;
+
+ case SD_BUS_TYPE_UNIX_FD: {
+ uint32_t j;
+
+ j = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
+ if (j >= m->n_fds)
+ return -EBADMSG;
+
+ if (p)
+ *(int*) p = m->fds[j];
+ break;
+ }
+
+ default:
+ assert_not_reached("Unknown basic type...");
+ }
+ }
+ }
+
+ m->rindex = rindex;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index++;
+
+ return 1;
+}
+
+static int bus_message_enter_array(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents,
+ uint32_t **array_size,
+ size_t *item_size,
+ size_t **offsets,
+ size_t *n_offsets) {
+
+ size_t rindex;
+ void *q;
+ int r, alignment;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+ assert(array_size);
+ assert(item_size);
+ assert(offsets);
+ assert(n_offsets);
+
+ if (!signature_is_single(contents, true))
+ return -EINVAL;
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return -ENXIO;
+
+ if (c->signature[c->index] != SD_BUS_TYPE_ARRAY)
+ return -ENXIO;
+
+ if (!startswith(c->signature + c->index + 1, contents))
+ return -ENXIO;
+
+ rindex = m->rindex;
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m)) {
+ /* dbus1 */
+
+ r = message_peek_body(m, &rindex, 4, 4, &q);
+ if (r < 0)
+ return r;
+
+ if (BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q) > BUS_ARRAY_MAX_SIZE)
+ return -EBADMSG;
+
+ alignment = bus_type_get_alignment(contents[0]);
+ if (alignment < 0)
+ return alignment;
+
+ r = message_peek_body(m, &rindex, alignment, 0, NULL);
+ if (r < 0)
+ return r;
+
+ *array_size = (uint32_t*) q;
+
+ } else if (c->item_size <= 0) {
+
+ /* gvariant: empty array */
+ *item_size = 0;
+ *offsets = NULL;
+ *n_offsets = 0;
+
+ } else if (bus_gvariant_is_fixed_size(contents)) {
+
+ /* gvariant: fixed length array */
+ *item_size = bus_gvariant_get_size(contents);
+ *offsets = NULL;
+ *n_offsets = 0;
+
+ } else {
+ size_t where, p = 0, framing, sz;
+ unsigned i;
+
+ /* gvariant: variable length array */
+ sz = determine_word_size(c->item_size, 0);
+
+ where = rindex + c->item_size - sz;
+ r = message_peek_body(m, &where, 1, sz, &q);
+ if (r < 0)
+ return r;
+
+ framing = read_word_le(q, sz);
+ if (framing > c->item_size - sz)
+ return -EBADMSG;
+ if ((c->item_size - framing) % sz != 0)
+ return -EBADMSG;
+
+ *n_offsets = (c->item_size - framing) / sz;
+
+ where = rindex + framing;
+ r = message_peek_body(m, &where, 1, *n_offsets * sz, &q);
+ if (r < 0)
+ return r;
+
+ *offsets = new(size_t, *n_offsets);
+ if (!*offsets)
+ return -ENOMEM;
+
+ for (i = 0; i < *n_offsets; i++) {
+ size_t x;
+
+ x = read_word_le((uint8_t*) q + i * sz, sz);
+ if (x > c->item_size - sz)
+ return -EBADMSG;
+ if (x < p)
+ return -EBADMSG;
+
+ (*offsets)[i] = rindex + x;
+ p = x;
+ }
+
+ *item_size = (*offsets)[0] - rindex;
+ }
+
+ m->rindex = rindex;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index += 1 + strlen(contents);
+
+ return 1;
+}
+
+static int bus_message_enter_variant(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents,
+ size_t *item_size) {
+
+ size_t rindex;
+ uint8_t l;
+ void *q;
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+ assert(item_size);
+
+ if (!signature_is_single(contents, false))
+ return -EINVAL;
+
+ if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN)
+ return -EINVAL;
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return -ENXIO;
+
+ if (c->signature[c->index] != SD_BUS_TYPE_VARIANT)
+ return -ENXIO;
+
+ rindex = m->rindex;
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ size_t k, where;
+
+ k = strlen(contents);
+ if (1+k > c->item_size)
+ return -EBADMSG;
+
+ where = rindex + c->item_size - (1+k);
+ r = message_peek_body(m, &where, 1, 1+k, &q);
+ if (r < 0)
+ return r;
+
+ if (*(char*) q != 0)
+ return -EBADMSG;
+
+ if (memcmp((uint8_t*) q+1, contents, k))
+ return -ENXIO;
+
+ *item_size = c->item_size - (1+k);
+
+ } else {
+ r = message_peek_body(m, &rindex, 1, 1, &q);
+ if (r < 0)
+ return r;
+
+ l = *(uint8_t*) q;
+ r = message_peek_body(m, &rindex, 1, l+1, &q);
+ if (r < 0)
+ return r;
+
+ if (!validate_signature(q, l))
+ return -EBADMSG;
+
+ if (!streq(q, contents))
+ return -ENXIO;
+ }
+
+ m->rindex = rindex;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index++;
+
+ return 1;
+}
+
+static int build_struct_offsets(
+ sd_bus_message *m,
+ const char *signature,
+ size_t size,
+ size_t *item_size,
+ size_t **offsets,
+ size_t *n_offsets) {
+
+ unsigned n_variable = 0, n_total = 0, v;
+ size_t previous = 0, where;
+ const char *p;
+ size_t sz;
+ void *q;
+ int r;
+
+ assert(m);
+ assert(item_size);
+ assert(offsets);
+ assert(n_offsets);
+
+ if (isempty(signature)) {
+ *item_size = 0;
+ *offsets = NULL;
+ *n_offsets = 0;
+ return 0;
+ }
+
+ sz = determine_word_size(size, 0);
+ if (sz <= 0)
+ return -EBADMSG;
+
+ /* First, loop over signature and count variable elements and
+ * elements in general. We use this to know how large the
+ * offset array is at the end of the structure. Note that
+ * GVariant only stores offsets for all variable size elements
+ * that are not the last item. */
+
+ p = signature;
+ while (*p != 0) {
+ size_t n;
+
+ r = signature_element_length(p, &n);
+ if (r < 0)
+ return r;
+ else {
+ char t[n+1];
+
+ memcpy(t, p, n);
+ t[n] = 0;
+
+ r = bus_gvariant_is_fixed_size(t);
+ }
+
+ if (r < 0)
+ return r;
+ if (r == 0 && p[n] != 0) /* except the last item */
+ n_variable ++;
+ n_total++;
+
+ p += n;
+ }
+
+ if (size < n_variable * sz)
+ return -EBADMSG;
+
+ where = m->rindex + size - (n_variable * sz);
+ r = message_peek_body(m, &where, 1, n_variable * sz, &q);
+ if (r < 0)
+ return r;
+
+ v = n_variable;
+
+ *offsets = new(size_t, n_total);
+ if (!*offsets)
+ return -ENOMEM;
+
+ *n_offsets = 0;
+
+ /* Second, loop again and build an offset table */
+ p = signature;
+ while (*p != 0) {
+ size_t n, offset;
+ int k;
+
+ r = signature_element_length(p, &n);
+ if (r < 0)
+ return r;
+ else {
+ char t[n+1];
+
+ memcpy(t, p, n);
+ t[n] = 0;
+
+ k = bus_gvariant_get_size(t);
+ if (k < 0) {
+ size_t x;
+
+ /* variable size */
+ if (v > 0) {
+ v--;
+
+ x = read_word_le((uint8_t*) q + v*sz, sz);
+ if (x >= size)
+ return -EBADMSG;
+ if (m->rindex + x < previous)
+ return -EBADMSG;
+ } else
+ /* The last item's end
+ * is determined from
+ * the start of the
+ * offset array */
+ x = size - (n_variable * sz);
+
+ offset = m->rindex + x;
+
+ } else {
+ size_t align;
+
+ /* fixed size */
+ align = bus_gvariant_get_alignment(t);
+ assert(align > 0);
+
+ offset = (*n_offsets == 0 ? m->rindex : ALIGN_TO((*offsets)[*n_offsets-1], align)) + k;
+ }
+ }
+
+ previous = (*offsets)[(*n_offsets)++] = offset;
+ p += n;
+ }
+
+ assert(v == 0);
+ assert(*n_offsets == n_total);
+
+ *item_size = (*offsets)[0] - m->rindex;
+ return 0;
+}
+
+static int enter_struct_or_dict_entry(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents,
+ size_t *item_size,
+ size_t **offsets,
+ size_t *n_offsets) {
+
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+ assert(item_size);
+ assert(offsets);
+ assert(n_offsets);
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m)) {
+
+ /* dbus1 */
+ r = message_peek_body(m, &m->rindex, 8, 0, NULL);
+ if (r < 0)
+ return r;
+
+ } else if (c->item_size <= 0) {
+
+ /* gvariant empty struct */
+ *item_size = 0;
+ *offsets = NULL;
+ *n_offsets = 0;
+ } else
+ /* gvariant with contents */
+ return build_struct_offsets(m, contents, c->item_size, item_size, offsets, n_offsets);
+
+ return 0;
+}
+
+static int bus_message_enter_struct(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents,
+ size_t *item_size,
+ size_t **offsets,
+ size_t *n_offsets) {
+
+ size_t l;
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+ assert(item_size);
+ assert(offsets);
+ assert(n_offsets);
+
+ if (!signature_is_valid(contents, false))
+ return -EINVAL;
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return -ENXIO;
+
+ l = strlen(contents);
+
+ if (c->signature[c->index] != SD_BUS_TYPE_STRUCT_BEGIN ||
+ !startswith(c->signature + c->index + 1, contents) ||
+ c->signature[c->index + 1 + l] != SD_BUS_TYPE_STRUCT_END)
+ return -ENXIO;
+
+ r = enter_struct_or_dict_entry(m, c, contents, item_size, offsets, n_offsets);
+ if (r < 0)
+ return r;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index += 1 + l + 1;
+
+ return 1;
+}
+
+static int bus_message_enter_dict_entry(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents,
+ size_t *item_size,
+ size_t **offsets,
+ size_t *n_offsets) {
+
+ size_t l;
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_pair(contents))
+ return -EINVAL;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ return -ENXIO;
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return 0;
+
+ l = strlen(contents);
+
+ if (c->signature[c->index] != SD_BUS_TYPE_DICT_ENTRY_BEGIN ||
+ !startswith(c->signature + c->index + 1, contents) ||
+ c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END)
+ return -ENXIO;
+
+ r = enter_struct_or_dict_entry(m, c, contents, item_size, offsets, n_offsets);
+ if (r < 0)
+ return r;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index += 1 + l + 1;
+
+ return 1;
+}
+
+_public_ int sd_bus_message_enter_container(sd_bus_message *m,
+ char type,
+ const char *contents) {
+ struct bus_container *c, *w;
+ uint32_t *array_size = NULL;
+ char *signature;
+ size_t before;
+ size_t *offsets = NULL;
+ size_t n_offsets = 0, item_size = 0;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+ assert_return(type != 0 || !contents, -EINVAL);
+
+ if (type == 0 || !contents) {
+ const char *cc;
+ char tt;
+
+ /* Allow entering into anonymous containers */
+ r = sd_bus_message_peek_type(m, &tt, &cc);
+ if (r < 0)
+ return r;
+
+ if (type != 0 && type != tt)
+ return -ENXIO;
+
+ if (contents && !streq(contents, cc))
+ return -ENXIO;
+
+ type = tt;
+ contents = cc;
+ }
+
+ /*
+ * We enforce a global limit on container depth, that is much
+ * higher than the 32 structs and 32 arrays the specification
+ * mandates. This is simpler to implement for us, and we need
+ * this only to ensure our container array doesn't grow
+ * without bounds. We are happy to return any data from a
+ * message as long as the data itself is valid, even if the
+ * overall message might be not.
+ *
+ * Note that the message signature is validated when
+ * parsing the headers, and that validation does check the
+ * 32/32 limit.
+ *
+ * Note that the specification defines no limits on the depth
+ * of stacked variants, but we do.
+ */
+ if (m->n_containers >= BUS_CONTAINER_DEPTH)
+ return -EBADMSG;
+
+ if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1))
+ return -ENOMEM;
+
+ if (message_end_of_signature(m))
+ return -ENXIO;
+
+ if (message_end_of_array(m, m->rindex))
+ return 0;
+
+ c = message_get_container(m);
+
+ signature = strdup(contents);
+ if (!signature)
+ return -ENOMEM;
+
+ c->saved_index = c->index;
+ before = m->rindex;
+
+ if (type == SD_BUS_TYPE_ARRAY)
+ r = bus_message_enter_array(m, c, contents, &array_size, &item_size, &offsets, &n_offsets);
+ else if (type == SD_BUS_TYPE_VARIANT)
+ r = bus_message_enter_variant(m, c, contents, &item_size);
+ else if (type == SD_BUS_TYPE_STRUCT)
+ r = bus_message_enter_struct(m, c, contents, &item_size, &offsets, &n_offsets);
+ else if (type == SD_BUS_TYPE_DICT_ENTRY)
+ r = bus_message_enter_dict_entry(m, c, contents, &item_size, &offsets, &n_offsets);
+ else
+ r = -EINVAL;
+
+ if (r <= 0) {
+ free(signature);
+ free(offsets);
+ return r;
+ }
+
+ /* OK, let's fill it in */
+ w = m->containers + m->n_containers++;
+ w->enclosing = type;
+ w->signature = signature;
+ w->peeked_signature = NULL;
+ w->index = 0;
+
+ w->before = before;
+ w->begin = m->rindex;
+ w->end = m->rindex + c->item_size;
+
+ w->array_size = array_size;
+ w->item_size = item_size;
+ w->offsets = offsets;
+ w->n_offsets = n_offsets;
+ w->offset_index = 0;
+
+ return 1;
+}
+
+_public_ int sd_bus_message_exit_container(sd_bus_message *m) {
+ struct bus_container *c;
+ unsigned saved;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+ assert_return(m->n_containers > 0, -ENXIO);
+
+ c = message_get_container(m);
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY) {
+ if (c->signature && c->signature[c->index] != 0)
+ return -EBUSY;
+ }
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ if (m->rindex < c->end)
+ return -EBUSY;
+
+ } else if (c->enclosing == SD_BUS_TYPE_ARRAY) {
+ uint32_t l;
+
+ l = BUS_MESSAGE_BSWAP32(m, *c->array_size);
+ if (c->begin + l != m->rindex)
+ return -EBUSY;
+ }
+
+ free(c->signature);
+ free(c->peeked_signature);
+ free(c->offsets);
+ m->n_containers--;
+
+ c = message_get_container(m);
+
+ saved = c->index;
+ c->index = c->saved_index;
+ r = container_next_item(m, c, &m->rindex);
+ c->index = saved;
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static void message_quit_container(sd_bus_message *m) {
+ struct bus_container *c;
+
+ assert(m);
+ assert(m->sealed);
+ assert(m->n_containers > 0);
+
+ c = message_get_container(m);
+
+ /* Undo seeks */
+ assert(m->rindex >= c->before);
+ m->rindex = c->before;
+
+ /* Free container */
+ free(c->signature);
+ free(c->offsets);
+ m->n_containers--;
+
+ /* Correct index of new top-level container */
+ c = message_get_container(m);
+ c->index = c->saved_index;
+}
+
+_public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char **contents) {
+ struct bus_container *c;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+
+ if (message_end_of_signature(m))
+ goto eof;
+
+ if (message_end_of_array(m, m->rindex))
+ goto eof;
+
+ c = message_get_container(m);
+
+ if (bus_type_is_basic(c->signature[c->index])) {
+ if (contents)
+ *contents = NULL;
+ if (type)
+ *type = c->signature[c->index];
+ return 1;
+ }
+
+ if (c->signature[c->index] == SD_BUS_TYPE_ARRAY) {
+
+ if (contents) {
+ size_t l;
+ char *sig;
+
+ r = signature_element_length(c->signature+c->index+1, &l);
+ if (r < 0)
+ return r;
+
+ assert(l >= 1);
+
+ sig = strndup(c->signature + c->index + 1, l);
+ if (!sig)
+ return -ENOMEM;
+
+ free(c->peeked_signature);
+ *contents = c->peeked_signature = sig;
+ }
+
+ if (type)
+ *type = SD_BUS_TYPE_ARRAY;
+
+ return 1;
+ }
+
+ if (c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN ||
+ c->signature[c->index] == SD_BUS_TYPE_DICT_ENTRY_BEGIN) {
+
+ if (contents) {
+ size_t l;
+ char *sig;
+
+ r = signature_element_length(c->signature+c->index, &l);
+ if (r < 0)
+ return r;
+
+ assert(l >= 2);
+ sig = strndup(c->signature + c->index + 1, l - 2);
+ if (!sig)
+ return -ENOMEM;
+
+ free(c->peeked_signature);
+ *contents = c->peeked_signature = sig;
+ }
+
+ if (type)
+ *type = c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY;
+
+ return 1;
+ }
+
+ if (c->signature[c->index] == SD_BUS_TYPE_VARIANT) {
+ if (contents) {
+ void *q;
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ size_t k;
+
+ if (c->item_size < 2)
+ return -EBADMSG;
+
+ /* Look for the NUL delimiter that
+ separates the payload from the
+ signature. Since the body might be
+ in a different part that then the
+ signature we map byte by byte. */
+
+ for (k = 2; k <= c->item_size; k++) {
+ size_t where;
+
+ where = m->rindex + c->item_size - k;
+ r = message_peek_body(m, &where, 1, k, &q);
+ if (r < 0)
+ return r;
+
+ if (*(char*) q == 0)
+ break;
+ }
+
+ if (k > c->item_size)
+ return -EBADMSG;
+
+ free(c->peeked_signature);
+ c->peeked_signature = strndup((char*) q + 1, k - 1);
+ if (!c->peeked_signature)
+ return -ENOMEM;
+
+ if (!signature_is_valid(c->peeked_signature, true))
+ return -EBADMSG;
+
+ *contents = c->peeked_signature;
+ } else {
+ size_t rindex, l;
+
+ rindex = m->rindex;
+ r = message_peek_body(m, &rindex, 1, 1, &q);
+ if (r < 0)
+ return r;
+
+ l = *(uint8_t*) q;
+ r = message_peek_body(m, &rindex, 1, l+1, &q);
+ if (r < 0)
+ return r;
+
+ if (!validate_signature(q, l))
+ return -EBADMSG;
+
+ *contents = q;
+ }
+ }
+
+ if (type)
+ *type = SD_BUS_TYPE_VARIANT;
+
+ return 1;
+ }
+
+ return -EINVAL;
+
+eof:
+ if (type)
+ *type = 0;
+ if (contents)
+ *contents = NULL;
+ return 0;
+}
+
+_public_ int sd_bus_message_rewind(sd_bus_message *m, int complete) {
+ struct bus_container *c;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+
+ if (complete) {
+ message_reset_containers(m);
+ m->rindex = 0;
+
+ c = message_get_container(m);
+ } else {
+ c = message_get_container(m);
+
+ c->offset_index = 0;
+ c->index = 0;
+ m->rindex = c->begin;
+ }
+
+ c->offset_index = 0;
+ c->item_size = (c->n_offsets > 0 ? c->offsets[0] : c->end) - c->begin;
+
+ return !isempty(c->signature);
+}
+
+static int message_read_ap(
+ sd_bus_message *m,
+ const char *types,
+ va_list ap) {
+
+ unsigned n_array, n_struct;
+ TypeStack stack[BUS_CONTAINER_DEPTH];
+ unsigned stack_ptr = 0;
+ unsigned n_loop = 0;
+ int r;
+
+ assert(m);
+
+ if (isempty(types))
+ return 0;
+
+ /* Ideally, we'd just call ourselves recursively on every
+ * complex type. However, the state of a va_list that is
+ * passed to a function is undefined after that function
+ * returns. This means we need to docode the va_list linearly
+ * in a single stackframe. We hence implement our own
+ * home-grown stack in an array. */
+
+ n_array = (unsigned) -1; /* lenght of current array entries */
+ n_struct = strlen(types); /* length of current struct contents signature */
+
+ for (;;) {
+ const char *t;
+
+ n_loop++;
+
+ if (n_array == 0 || (n_array == (unsigned) -1 && n_struct == 0)) {
+ r = type_stack_pop(stack, ELEMENTSOF(stack), &stack_ptr, &types, &n_struct, &n_array);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ continue;
+ }
+
+ t = types;
+ if (n_array != (unsigned) -1)
+ n_array --;
+ else {
+ types ++;
+ n_struct--;
+ }
+
+ switch (*t) {
+
+ case SD_BUS_TYPE_BYTE:
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE:
+ case SD_BUS_TYPE_UNIX_FD: {
+ void *p;
+
+ p = va_arg(ap, void*);
+ r = sd_bus_message_read_basic(m, *t, p);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (n_loop <= 1)
+ return 0;
+
+ return -ENXIO;
+ }
+
+ break;
+ }
+
+ case SD_BUS_TYPE_ARRAY: {
+ size_t k;
+
+ r = signature_element_length(t + 1, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k + 1];
+ memcpy(s, t + 1, k);
+ s[k] = 0;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, s);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (n_loop <= 1)
+ return 0;
+
+ return -ENXIO;
+ }
+ }
+
+ if (n_array == (unsigned) -1) {
+ types += k;
+ n_struct -= k;
+ }
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = t + 1;
+ n_struct = k;
+ n_array = va_arg(ap, unsigned);
+
+ break;
+ }
+
+ case SD_BUS_TYPE_VARIANT: {
+ const char *s;
+
+ s = va_arg(ap, const char *);
+ if (!s)
+ return -EINVAL;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, s);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (n_loop <= 1)
+ return 0;
+
+ return -ENXIO;
+ }
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = s;
+ n_struct = strlen(s);
+ n_array = (unsigned) -1;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
+ size_t k;
+
+ r = signature_element_length(t, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k - 1];
+ memcpy(s, t + 1, k - 2);
+ s[k - 2] = 0;
+
+ r = sd_bus_message_enter_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (n_loop <= 1)
+ return 0;
+ return -ENXIO;
+ }
+ }
+
+ if (n_array == (unsigned) -1) {
+ types += k - 1;
+ n_struct -= k - 1;
+ }
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = t + 1;
+ n_struct = k - 2;
+ n_array = (unsigned) -1;
+
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 1;
+}
+
+_public_ int sd_bus_message_read(sd_bus_message *m, const char *types, ...) {
+ va_list ap;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+ assert_return(types, -EINVAL);
+
+ va_start(ap, types);
+ r = message_read_ap(m, types, ap);
+ va_end(ap);
+
+ return r;
+}
+
+_public_ int sd_bus_message_skip(sd_bus_message *m, const char *types) {
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+ assert_return(types, -EINVAL);
+
+ if (isempty(types))
+ return 0;
+
+ switch (*types) {
+
+ case SD_BUS_TYPE_BYTE:
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE:
+ case SD_BUS_TYPE_UNIX_FD:
+
+ r = sd_bus_message_read_basic(m, *types, NULL);
+ if (r <= 0)
+ return r;
+
+ r = sd_bus_message_skip(m, types + 1);
+ if (r < 0)
+ return r;
+
+ return 1;
+
+ case SD_BUS_TYPE_ARRAY: {
+ size_t k;
+
+ r = signature_element_length(types + 1, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k+1];
+ memcpy(s, types+1, k);
+ s[k] = 0;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, s);
+ if (r <= 0)
+ return r;
+
+ for (;;) {
+ r = sd_bus_message_skip(m, s);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_skip(m, types + 1 + k);
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ case SD_BUS_TYPE_VARIANT: {
+ const char *contents;
+ char x;
+
+ r = sd_bus_message_peek_type(m, &x, &contents);
+ if (r <= 0)
+ return r;
+
+ if (x != SD_BUS_TYPE_VARIANT)
+ return -ENXIO;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
+ if (r <= 0)
+ return r;
+
+ r = sd_bus_message_skip(m, contents);
+ if (r < 0)
+ return r;
+ assert(r != 0);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_skip(m, types + 1);
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
+ size_t k;
+
+ r = signature_element_length(types, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k-1];
+ memcpy(s, types+1, k-2);
+ s[k-2] = 0;
+
+ r = sd_bus_message_enter_container(m, *types == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
+ if (r <= 0)
+ return r;
+
+ r = sd_bus_message_skip(m, s);
+ if (r < 0)
+ return r;
+ assert(r != 0);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_skip(m, types + k);
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+_public_ int sd_bus_message_read_array(sd_bus_message *m,
+ char type,
+ const void **ptr,
+ size_t *size) {
+ struct bus_container *c;
+ void *p;
+ size_t sz;
+ ssize_t align;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+ assert_return(bus_type_is_trivial(type), -EINVAL);
+ assert_return(ptr, -EINVAL);
+ assert_return(size, -EINVAL);
+ assert_return(!BUS_MESSAGE_NEED_BSWAP(m), -ENOTSUP);
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type));
+ if (r <= 0)
+ return r;
+
+ c = message_get_container(m);
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ align = bus_gvariant_get_alignment(CHAR_TO_STR(type));
+ if (align < 0)
+ return align;
+
+ sz = c->end - c->begin;
+ } else {
+ align = bus_type_get_alignment(type);
+ if (align < 0)
+ return align;
+
+ sz = BUS_MESSAGE_BSWAP32(m, *c->array_size);
+ }
+
+ if (sz == 0)
+ /* Zero length array, let's return some aligned
+ * pointer that is not NULL */
+ p = (uint8_t*) NULL + align;
+ else {
+ r = message_peek_body(m, &m->rindex, align, sz, &p);
+ if (r < 0)
+ goto fail;
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ goto fail;
+
+ *ptr = (const void*) p;
+ *size = sz;
+
+ return 1;
+
+fail:
+ message_quit_container(m);
+ return r;
+}
+
+static int message_peek_fields(
+ sd_bus_message *m,
+ size_t *rindex,
+ size_t align,
+ size_t nbytes,
+ void **ret) {
+
+ assert(m);
+ assert(rindex);
+ assert(align > 0);
+
+ return buffer_peek(BUS_MESSAGE_FIELDS(m), BUS_MESSAGE_FIELDS_SIZE(m), rindex, align, nbytes, ret);
+}
+
+static int message_peek_field_uint32(
+ sd_bus_message *m,
+ size_t *ri,
+ size_t item_size,
+ uint32_t *ret) {
+
+ int r;
+ void *q;
+
+ assert(m);
+ assert(ri);
+
+ if (BUS_MESSAGE_IS_GVARIANT(m) && item_size != 4)
+ return -EBADMSG;
+
+ /* identical for gvariant and dbus1 */
+
+ r = message_peek_fields(m, ri, 4, 4, &q);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
+
+ return 0;
+}
+
+static int message_peek_field_string(
+ sd_bus_message *m,
+ bool (*validate)(const char *p),
+ size_t *ri,
+ size_t item_size,
+ const char **ret) {
+
+ uint32_t l;
+ int r;
+ void *q;
+
+ assert(m);
+ assert(ri);
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+
+ if (item_size <= 0)
+ return -EBADMSG;
+
+ r = message_peek_fields(m, ri, 1, item_size, &q);
+ if (r < 0)
+ return r;
+
+ l = item_size - 1;
+ } else {
+ r = message_peek_field_uint32(m, ri, 4, &l);
+ if (r < 0)
+ return r;
+
+ r = message_peek_fields(m, ri, 1, l+1, &q);
+ if (r < 0)
+ return r;
+ }
+
+ if (validate) {
+ if (!validate_nul(q, l))
+ return -EBADMSG;
+
+ if (!validate(q))
+ return -EBADMSG;
+ } else {
+ if (!validate_string(q, l))
+ return -EBADMSG;
+ }
+
+ if (ret)
+ *ret = q;
+
+ return 0;
+}
+
+static int message_peek_field_signature(
+ sd_bus_message *m,
+ size_t *ri,
+ size_t item_size,
+ const char **ret) {
+
+ size_t l;
+ int r;
+ void *q;
+
+ assert(m);
+ assert(ri);
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+
+ if (item_size <= 0)
+ return -EBADMSG;
+
+ r = message_peek_fields(m, ri, 1, item_size, &q);
+ if (r < 0)
+ return r;
+
+ l = item_size - 1;
+ } else {
+ r = message_peek_fields(m, ri, 1, 1, &q);
+ if (r < 0)
+ return r;
+
+ l = *(uint8_t*) q;
+ r = message_peek_fields(m, ri, 1, l+1, &q);
+ if (r < 0)
+ return r;
+ }
+
+ if (!validate_signature(q, l))
+ return -EBADMSG;
+
+ if (ret)
+ *ret = q;
+
+ return 0;
+}
+
+static int message_skip_fields(
+ sd_bus_message *m,
+ size_t *ri,
+ uint32_t array_size,
+ const char **signature) {
+
+ size_t original_index;
+ int r;
+
+ assert(m);
+ assert(ri);
+ assert(signature);
+ assert(!BUS_MESSAGE_IS_GVARIANT(m));
+
+ original_index = *ri;
+
+ for (;;) {
+ char t;
+ size_t l;
+
+ if (array_size != (uint32_t) -1 &&
+ array_size <= *ri - original_index)
+ return 0;
+
+ t = **signature;
+ if (!t)
+ return 0;
+
+ if (t == SD_BUS_TYPE_STRING) {
+
+ r = message_peek_field_string(m, NULL, ri, 0, NULL);
+ if (r < 0)
+ return r;
+
+ (*signature)++;
+
+ } else if (t == SD_BUS_TYPE_OBJECT_PATH) {
+
+ r = message_peek_field_string(m, object_path_is_valid, ri, 0, NULL);
+ if (r < 0)
+ return r;
+
+ (*signature)++;
+
+ } else if (t == SD_BUS_TYPE_SIGNATURE) {
+
+ r = message_peek_field_signature(m, ri, 0, NULL);
+ if (r < 0)
+ return r;
+
+ (*signature)++;
+
+ } else if (bus_type_is_basic(t)) {
+ ssize_t align, k;
+
+ align = bus_type_get_alignment(t);
+ k = bus_type_get_size(t);
+ assert(align > 0 && k > 0);
+
+ r = message_peek_fields(m, ri, align, k, NULL);
+ if (r < 0)
+ return r;
+
+ (*signature)++;
+
+ } else if (t == SD_BUS_TYPE_ARRAY) {
+
+ r = signature_element_length(*signature+1, &l);
+ if (r < 0)
+ return r;
+
+ assert(l >= 1);
+ {
+ char sig[l-1], *s;
+ uint32_t nas;
+ int alignment;
+
+ strncpy(sig, *signature + 1, l-1);
+ s = sig;
+
+ alignment = bus_type_get_alignment(sig[0]);
+ if (alignment < 0)
+ return alignment;
+
+ r = message_peek_field_uint32(m, ri, 0, &nas);
+ if (r < 0)
+ return r;
+ if (nas > BUS_ARRAY_MAX_SIZE)
+ return -EBADMSG;
+
+ r = message_peek_fields(m, ri, alignment, 0, NULL);
+ if (r < 0)
+ return r;
+
+ r = message_skip_fields(m, ri, nas, (const char**) &s);
+ if (r < 0)
+ return r;
+ }
+
+ (*signature) += 1 + l;
+
+ } else if (t == SD_BUS_TYPE_VARIANT) {
+ const char *s;
+
+ r = message_peek_field_signature(m, ri, 0, &s);
+ if (r < 0)
+ return r;
+
+ r = message_skip_fields(m, ri, (uint32_t) -1, (const char**) &s);
+ if (r < 0)
+ return r;
+
+ (*signature)++;
+
+ } else if (t == SD_BUS_TYPE_STRUCT ||
+ t == SD_BUS_TYPE_DICT_ENTRY) {
+
+ r = signature_element_length(*signature, &l);
+ if (r < 0)
+ return r;
+
+ assert(l >= 2);
+ {
+ char sig[l-1], *s;
+ strncpy(sig, *signature + 1, l-1);
+ s = sig;
+
+ r = message_skip_fields(m, ri, (uint32_t) -1, (const char**) &s);
+ if (r < 0)
+ return r;
+ }
+
+ *signature += l;
+ } else
+ return -EINVAL;
+ }
+}
+
+int bus_message_parse_fields(sd_bus_message *m) {
+ size_t ri;
+ int r;
+ uint32_t unix_fds = 0;
+ void *offsets = NULL;
+ unsigned n_offsets = 0;
+ size_t sz = 0;
+ unsigned i = 0;
+
+ assert(m);
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ void *q;
+
+ sz = determine_word_size(BUS_MESSAGE_FIELDS_SIZE(m), 0);
+ if (sz > 0) {
+ size_t framing;
+
+ ri = BUS_MESSAGE_FIELDS_SIZE(m) - sz;
+ r = message_peek_fields(m, &ri, 1, sz, &q);
+ if (r < 0)
+ return r;
+
+ framing = read_word_le(q, sz);
+ if (framing >= BUS_MESSAGE_FIELDS_SIZE(m) - sz)
+ return -EBADMSG;
+ if ((BUS_MESSAGE_FIELDS_SIZE(m) - framing) % sz != 0)
+ return -EBADMSG;
+
+ ri = framing;
+ r = message_peek_fields(m, &ri, 1, BUS_MESSAGE_FIELDS_SIZE(m) - framing, &offsets);
+ if (r < 0)
+ return r;
+
+ n_offsets = (BUS_MESSAGE_FIELDS_SIZE(m) - framing) / sz;
+ }
+ }
+
+ ri = 0;
+ while (ri < BUS_MESSAGE_FIELDS_SIZE(m)) {
+ _cleanup_free_ char *sig = NULL;
+ const char *signature;
+ uint8_t *header;
+ size_t item_size = (size_t) -1;
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ if (i >= n_offsets)
+ break;
+
+ if (i == 0)
+ ri = 0;
+ else
+ ri = ALIGN_TO(read_word_le((uint8_t*) offsets + (i-1)*sz, sz), 8);
+ }
+
+ r = message_peek_fields(m, &ri, 8, 1, (void**) &header);
+ if (r < 0)
+ return r;
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ size_t where, end;
+ char *b;
+ void *q;
+
+ end = read_word_le((uint8_t*) offsets + i*sz, sz);
+
+ if (end < ri)
+ return -EBADMSG;
+
+ where = ri = ALIGN_TO(ri, 8);
+ item_size = end - ri;
+ r = message_peek_fields(m, &where, 1, item_size, &q);
+ if (r < 0)
+ return r;
+
+ b = memrchr(q, 0, item_size);
+ if (!b)
+ return -EBADMSG;
+
+ sig = strndup(b+1, item_size - (b+1-(char*) q));
+ if (!sig)
+ return -ENOMEM;
+
+ signature = sig;
+ item_size = b - (char*) q;
+ } else {
+ r = message_peek_field_signature(m, &ri, 0, &signature);
+ if (r < 0)
+ return r;
+ }
+
+ switch (*header) {
+ case _BUS_MESSAGE_HEADER_INVALID:
+ return -EBADMSG;
+
+ case BUS_MESSAGE_HEADER_PATH:
+
+ if (m->path)
+ return -EBADMSG;
+
+ if (!streq(signature, "o"))
+ return -EBADMSG;
+
+ r = message_peek_field_string(m, object_path_is_valid, &ri, item_size, &m->path);
+ break;
+
+ case BUS_MESSAGE_HEADER_INTERFACE:
+
+ if (m->interface)
+ return -EBADMSG;
+
+ if (!streq(signature, "s"))
+ return -EBADMSG;
+
+ r = message_peek_field_string(m, interface_name_is_valid, &ri, item_size, &m->interface);
+ break;
+
+ case BUS_MESSAGE_HEADER_MEMBER:
+
+ if (m->member)
+ return -EBADMSG;
+
+ if (!streq(signature, "s"))
+ return -EBADMSG;
+
+ r = message_peek_field_string(m, member_name_is_valid, &ri, item_size, &m->member);
+ break;
+
+ case BUS_MESSAGE_HEADER_ERROR_NAME:
+
+ if (m->error.name)
+ return -EBADMSG;
+
+ if (!streq(signature, "s"))
+ return -EBADMSG;
+
+ r = message_peek_field_string(m, error_name_is_valid, &ri, item_size, &m->error.name);
+ if (r >= 0)
+ m->error._need_free = -1;
+
+ break;
+
+ case BUS_MESSAGE_HEADER_DESTINATION:
+
+ if (m->destination)
+ return -EBADMSG;
+
+ if (!streq(signature, "s"))
+ return -EBADMSG;
+
+ r = message_peek_field_string(m, service_name_is_valid, &ri, item_size, &m->destination);
+ break;
+
+ case BUS_MESSAGE_HEADER_SENDER:
+
+ if (m->sender)
+ return -EBADMSG;
+
+ if (!streq(signature, "s"))
+ return -EBADMSG;
+
+ r = message_peek_field_string(m, service_name_is_valid, &ri, item_size, &m->sender);
+
+ if (r >= 0 && m->sender[0] == ':' && m->bus && m->bus->bus_client && !m->bus->is_kernel) {
+ m->creds.unique_name = (char*) m->sender;
+ m->creds.mask |= SD_BUS_CREDS_UNIQUE_NAME & m->bus->creds_mask;
+ }
+
+ break;
+
+
+ case BUS_MESSAGE_HEADER_SIGNATURE: {
+ const char *s;
+ char *c;
+
+ if (m->root_container.signature)
+ return -EBADMSG;
+
+ if (!streq(signature, "g"))
+ return -EBADMSG;
+
+ r = message_peek_field_signature(m, &ri, item_size, &s);
+ if (r < 0)
+ return r;
+
+ c = strdup(s);
+ if (!c)
+ return -ENOMEM;
+
+ free(m->root_container.signature);
+ m->root_container.signature = c;
+ break;
+ }
+
+ case BUS_MESSAGE_HEADER_REPLY_SERIAL:
+ if (m->reply_cookie != 0)
+ return -EBADMSG;
+
+ if (!streq(signature, "u"))
+ return -EBADMSG;
+
+ r = message_peek_field_uint32(m, &ri, item_size, &m->reply_cookie);
+ if (r < 0)
+ return r;
+
+ if (m->reply_cookie == 0)
+ return -EBADMSG;
+
+ break;
+
+ case BUS_MESSAGE_HEADER_UNIX_FDS:
+ if (unix_fds != 0)
+ return -EBADMSG;
+
+ if (!streq(signature, "u"))
+ return -EBADMSG;
+
+ r = message_peek_field_uint32(m, &ri, item_size, &unix_fds);
+ if (r < 0)
+ return -EBADMSG;
+
+ if (unix_fds == 0)
+ return -EBADMSG;
+
+ break;
+
+ default:
+ if (!BUS_MESSAGE_IS_GVARIANT(m))
+ r = message_skip_fields(m, &ri, (uint32_t) -1, (const char **) &signature);
+ }
+
+ if (r < 0)
+ return r;
+
+ i++;
+ }
+
+ if (m->n_fds != unix_fds)
+ return -EBADMSG;
+
+ switch (m->header->type) {
+
+ case SD_BUS_MESSAGE_SIGNAL:
+ if (!m->path || !m->interface || !m->member)
+ return -EBADMSG;
+ break;
+
+ case SD_BUS_MESSAGE_METHOD_CALL:
+
+ if (!m->path || !m->member)
+ return -EBADMSG;
+
+ break;
+
+ case SD_BUS_MESSAGE_METHOD_RETURN:
+
+ if (m->reply_cookie == 0)
+ return -EBADMSG;
+ break;
+
+ case SD_BUS_MESSAGE_METHOD_ERROR:
+
+ if (m->reply_cookie == 0 || !m->error.name)
+ return -EBADMSG;
+ break;
+ }
+
+ /* Refuse non-local messages that claim they are local */
+ if (streq_ptr(m->path, "/org/freedesktop/DBus/Local"))
+ return -EBADMSG;
+ if (streq_ptr(m->interface, "org.freedesktop.DBus.Local"))
+ return -EBADMSG;
+ if (streq_ptr(m->sender, "org.freedesktop.DBus.Local"))
+ return -EBADMSG;
+
+ m->root_container.end = BUS_MESSAGE_BODY_SIZE(m);
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ r = build_struct_offsets(
+ m,
+ m->root_container.signature,
+ BUS_MESSAGE_BODY_SIZE(m),
+ &m->root_container.item_size,
+ &m->root_container.offsets,
+ &m->root_container.n_offsets);
+ if (r < 0)
+ return r;
+ }
+
+ /* Try to read the error message, but if we can't it's a non-issue */
+ if (m->header->type == SD_BUS_MESSAGE_METHOD_ERROR)
+ sd_bus_message_read(m, "s", &m->error.message);
+
+ return 0;
+}
+
+_public_ int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) {
+ assert_return(m, -EINVAL);
+ assert_return(destination, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(!m->destination, -EEXIST);
+
+ return message_append_field_string(m, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &m->destination);
+}
+
+int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) {
+ size_t total;
+ void *p, *e;
+ unsigned i;
+ struct bus_body_part *part;
+
+ assert(m);
+ assert(buffer);
+ assert(sz);
+
+ total = BUS_MESSAGE_SIZE(m);
+
+ p = malloc(total);
+ if (!p)
+ return -ENOMEM;
+
+ e = mempcpy(p, m->header, BUS_MESSAGE_BODY_BEGIN(m));
+ MESSAGE_FOREACH_PART(part, i, m)
+ e = mempcpy(e, part->data, part->size);
+
+ assert(total == (size_t) ((uint8_t*) e - (uint8_t*) p));
+
+ *buffer = p;
+ *sz = total;
+
+ return 0;
+}
+
+int bus_message_read_strv_extend(sd_bus_message *m, char ***l) {
+ int r;
+
+ assert(m);
+ assert(l);
+
+ r = sd_bus_message_enter_container(m, 'a', "s");
+ if (r <= 0)
+ return r;
+
+ for (;;) {
+ const char *s;
+
+ r = sd_bus_message_read_basic(m, 's', &s);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = strv_extend(l, s);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+_public_ int sd_bus_message_read_strv(sd_bus_message *m, char ***l) {
+ char **strv = NULL;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+ assert_return(l, -EINVAL);
+
+ r = bus_message_read_strv_extend(m, &strv);
+ if (r <= 0) {
+ strv_free(strv);
+ return r;
+ }
+
+ *l = strv;
+ return 1;
+}
+
+const char* bus_message_get_arg(sd_bus_message *m, unsigned i) {
+ int r;
+ const char *t = NULL;
+ unsigned j;
+
+ assert(m);
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return NULL;
+
+ for (j = 0; j <= i; j++) {
+ char type;
+
+ r = sd_bus_message_peek_type(m, &type, NULL);
+ if (r < 0)
+ return NULL;
+
+ if (type != SD_BUS_TYPE_STRING &&
+ type != SD_BUS_TYPE_OBJECT_PATH &&
+ type != SD_BUS_TYPE_SIGNATURE)
+ return NULL;
+
+ r = sd_bus_message_read_basic(m, type, &t);
+ if (r < 0)
+ return NULL;
+ }
+
+ return t;
+}
+
+bool bus_header_is_complete(struct bus_header *h, size_t size) {
+ size_t full;
+
+ assert(h);
+ assert(size);
+
+ if (size < sizeof(struct bus_header))
+ return false;
+
+ full = sizeof(struct bus_header) +
+ (h->endian == BUS_NATIVE_ENDIAN ? h->fields_size : bswap_32(h->fields_size));
+
+ return size >= full;
+}
+
+int bus_header_message_size(struct bus_header *h, size_t *sum) {
+ size_t fs, bs;
+
+ assert(h);
+ assert(sum);
+
+ if (h->endian == BUS_NATIVE_ENDIAN) {
+ fs = h->fields_size;
+ bs = h->body_size;
+ } else if (h->endian == BUS_REVERSE_ENDIAN) {
+ fs = bswap_32(h->fields_size);
+ bs = bswap_32(h->body_size);
+ } else
+ return -EBADMSG;
+
+ *sum = sizeof(struct bus_header) + ALIGN8(fs) + bs;
+ return 0;
+}
+
+_public_ int sd_bus_message_get_errno(sd_bus_message *m) {
+ assert_return(m, -EINVAL);
+
+ if (m->header->type != SD_BUS_MESSAGE_METHOD_ERROR)
+ return 0;
+
+ return sd_bus_error_get_errno(&m->error);
+}
+
+_public_ const char* sd_bus_message_get_signature(sd_bus_message *m, int complete) {
+ struct bus_container *c;
+
+ assert_return(m, NULL);
+
+ c = complete ? &m->root_container : message_get_container(m);
+ return strempty(c->signature);
+}
+
+_public_ int sd_bus_message_copy(sd_bus_message *m, sd_bus_message *source, int all) {
+ bool done_something = false;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(source, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(source->sealed, -EPERM);
+
+ do {
+ const char *contents;
+ char type;
+ union {
+ uint8_t u8;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ int32_t s32;
+ uint64_t u64;
+ int64_t s64;
+ double d64;
+ const char *string;
+ int i;
+ } basic;
+
+ r = sd_bus_message_peek_type(source, &type, &contents);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ done_something = true;
+
+ if (bus_type_is_container(type) > 0) {
+
+ r = sd_bus_message_enter_container(source, type, contents);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(m, type, contents);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_copy(m, source, true);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(source);
+ if (r < 0)
+ return r;
+
+ continue;
+ }
+
+ r = sd_bus_message_read_basic(source, type, &basic);
+ if (r < 0)
+ return r;
+
+ assert(r > 0);
+
+ if (type == SD_BUS_TYPE_OBJECT_PATH ||
+ type == SD_BUS_TYPE_SIGNATURE ||
+ type == SD_BUS_TYPE_STRING)
+ r = sd_bus_message_append_basic(m, type, basic.string);
+ else
+ r = sd_bus_message_append_basic(m, type, &basic);
+
+ if (r < 0)
+ return r;
+
+ } while (all);
+
+ return done_something;
+}
+
+_public_ int sd_bus_message_verify_type(sd_bus_message *m, char type, const char *contents) {
+ const char *c;
+ char t;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+ assert_return(!type || bus_type_is_valid(type), -EINVAL);
+ assert_return(!contents || signature_is_valid(contents, true), -EINVAL);
+ assert_return(type || contents, -EINVAL);
+ assert_return(!contents || !type || bus_type_is_container(type), -EINVAL);
+
+ r = sd_bus_message_peek_type(m, &t, &c);
+ if (r <= 0)
+ return r;
+
+ if (type != 0 && type != t)
+ return 0;
+
+ if (contents && !streq_ptr(contents, c))
+ return 0;
+
+ return 1;
+}
+
+_public_ sd_bus *sd_bus_message_get_bus(sd_bus_message *m) {
+ assert_return(m, NULL);
+
+ return m->bus;
+}
+
+int bus_message_remarshal(sd_bus *bus, sd_bus_message **m) {
+ _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
+ usec_t timeout;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(*m);
+
+ switch ((*m)->header->type) {
+
+ case SD_BUS_MESSAGE_SIGNAL:
+ r = sd_bus_message_new_signal(bus, (*m)->path, (*m)->interface, (*m)->member, &n);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case SD_BUS_MESSAGE_METHOD_CALL:
+ r = sd_bus_message_new_method_call(bus, (*m)->destination, (*m)->path, (*m)->interface, (*m)->member, &n);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case SD_BUS_MESSAGE_METHOD_RETURN:
+ case SD_BUS_MESSAGE_METHOD_ERROR:
+
+ n = message_new(bus, (*m)->header->type);
+ if (!n)
+ return -ENOMEM;
+
+ n->reply_cookie = (*m)->reply_cookie;
+ r = message_append_field_uint32(n, BUS_MESSAGE_HEADER_REPLY_SERIAL, n->reply_cookie);
+ if (r < 0)
+ return r;
+
+ if ((*m)->header->type == SD_BUS_MESSAGE_METHOD_ERROR && (*m)->error.name) {
+ r = message_append_field_string(n, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, (*m)->error.name, &n->error.message);
+ if (r < 0)
+ return r;
+
+ n->error._need_free = -1;
+ }
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if ((*m)->destination && !n->destination) {
+ r = message_append_field_string(n, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, (*m)->destination, &n->destination);
+ if (r < 0)
+ return r;
+ }
+
+ if ((*m)->sender && !n->sender) {
+ r = message_append_field_string(n, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, (*m)->sender, &n->sender);
+ if (r < 0)
+ return r;
+ }
+
+ n->header->flags |= (*m)->header->flags & (BUS_MESSAGE_NO_REPLY_EXPECTED|BUS_MESSAGE_NO_AUTO_START);
+
+ r = sd_bus_message_copy(n, *m, true);
+ if (r < 0)
+ return r;
+
+ timeout = (*m)->timeout;
+ if (timeout == 0 && !((*m)->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED))
+ timeout = BUS_DEFAULT_TIMEOUT;
+
+ r = bus_message_seal(n, BUS_MESSAGE_COOKIE(*m), timeout);
+ if (r < 0)
+ return r;
+
+ sd_bus_message_unref(*m);
+ *m = n;
+ n = NULL;
+
+ return 0;
+}
+
+int bus_message_append_sender(sd_bus_message *m, const char *sender) {
+ assert(m);
+ assert(sender);
+
+ assert_return(!m->sealed, -EPERM);
+ assert_return(!m->sender, -EPERM);
+
+ return message_append_field_string(m, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, sender, &m->sender);
+}
diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h
new file mode 100644
index 0000000..5322375
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-message.h
@@ -0,0 +1,244 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <byteswap.h>
+#include <sys/socket.h>
+
+#include "macro.h"
+#include "sd-bus.h"
+#include "kdbus.h"
+#include "time-util.h"
+#include "bus-creds.h"
+#include "bus-protocol.h"
+
+struct bus_container {
+ char enclosing;
+ bool need_offsets:1;
+
+ /* Indexes into the signature string */
+ unsigned index, saved_index;
+ char *signature;
+
+ size_t before, begin, end;
+
+ /* dbus1: pointer to the array size value, if this is a value */
+ uint32_t *array_size;
+
+ /* gvariant: list of offsets to end of children if this is struct/dict entry/array */
+ size_t *offsets, n_offsets, offsets_allocated, offset_index;
+ size_t item_size;
+
+ char *peeked_signature;
+};
+
+struct bus_header {
+ uint8_t endian;
+ uint8_t type;
+ uint8_t flags;
+ uint8_t version;
+ uint32_t body_size;
+
+ /* Note that what the bus spec calls "serial" we'll call
+ "cookie" instead, because we don't want to imply that the
+ cookie was in any way monotonically increasing. */
+ uint32_t serial;
+ uint32_t fields_size;
+} _packed_;
+
+struct bus_body_part {
+ struct bus_body_part *next;
+ void *data;
+ size_t size;
+ size_t mapped;
+ size_t allocated;
+ int memfd;
+ bool free_this:1;
+ bool munmap_this:1;
+ bool sealed:1;
+ bool is_zero:1;
+};
+
+struct sd_bus_message {
+ unsigned n_ref;
+
+ sd_bus *bus;
+
+ uint32_t reply_cookie;
+
+ const char *path;
+ const char *interface;
+ const char *member;
+ const char *destination;
+ const char *sender;
+
+ sd_bus_error error;
+
+ sd_bus_creds creds;
+
+ usec_t monotonic;
+ usec_t realtime;
+
+ bool sealed:1;
+ bool dont_send:1;
+ bool allow_fds:1;
+ bool free_header:1;
+ bool free_kdbus:1;
+ bool free_fds:1;
+ bool release_kdbus:1;
+ bool poisoned:1;
+
+ struct bus_header *header;
+ struct bus_body_part body;
+ struct bus_body_part *body_end;
+ unsigned n_body_parts;
+
+ size_t rindex;
+ struct bus_body_part *cached_rindex_part;
+ size_t cached_rindex_part_begin;
+
+ uint32_t n_fds;
+ int *fds;
+
+ struct bus_container root_container, *containers;
+ unsigned n_containers;
+ size_t containers_allocated;
+
+ struct iovec *iovec;
+ struct iovec iovec_fixed[2];
+ unsigned n_iovec;
+
+ struct kdbus_msg *kdbus;
+
+ char *peeked_signature;
+
+ /* If set replies to this message must carry the signature
+ * specified here to successfully seal. This is initialized
+ * from the vtable data */
+ const char *enforced_reply_signature;
+
+ usec_t timeout;
+
+ char sender_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1];
+ char destination_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1];
+
+ size_t header_offsets[_BUS_MESSAGE_HEADER_MAX];
+ unsigned n_header_offsets;
+};
+
+#define BUS_MESSAGE_NEED_BSWAP(m) ((m)->header->endian != BUS_NATIVE_ENDIAN)
+
+static inline uint16_t BUS_MESSAGE_BSWAP16(sd_bus_message *m, uint16_t u) {
+ return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_16(u) : u;
+}
+
+static inline uint32_t BUS_MESSAGE_BSWAP32(sd_bus_message *m, uint32_t u) {
+ return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_32(u) : u;
+}
+
+static inline uint64_t BUS_MESSAGE_BSWAP64(sd_bus_message *m, uint64_t u) {
+ return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_64(u) : u;
+}
+
+static inline uint32_t BUS_MESSAGE_COOKIE(sd_bus_message *m) {
+ return BUS_MESSAGE_BSWAP32(m, m->header->serial);
+}
+
+static inline uint32_t BUS_MESSAGE_BODY_SIZE(sd_bus_message *m) {
+ return BUS_MESSAGE_BSWAP32(m, m->header->body_size);
+}
+
+static inline uint32_t BUS_MESSAGE_FIELDS_SIZE(sd_bus_message *m) {
+ return BUS_MESSAGE_BSWAP32(m, m->header->fields_size);
+}
+
+static inline uint32_t BUS_MESSAGE_SIZE(sd_bus_message *m) {
+ return
+ sizeof(struct bus_header) +
+ ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) +
+ BUS_MESSAGE_BODY_SIZE(m);
+}
+
+static inline uint32_t BUS_MESSAGE_BODY_BEGIN(sd_bus_message *m) {
+ return
+ sizeof(struct bus_header) +
+ ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m));
+}
+
+static inline void* BUS_MESSAGE_FIELDS(sd_bus_message *m) {
+ return (uint8_t*) m->header + sizeof(struct bus_header);
+}
+
+static inline bool BUS_MESSAGE_IS_GVARIANT(sd_bus_message *m) {
+ return m->header->version == 2;
+}
+
+int bus_message_seal(sd_bus_message *m, uint64_t serial, usec_t timeout);
+int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz);
+int bus_message_read_strv_extend(sd_bus_message *m, char ***l);
+
+int bus_message_from_header(
+ sd_bus *bus,
+ void *header,
+ size_t length,
+ int *fds,
+ unsigned n_fds,
+ const struct ucred *ucred,
+ const char *label,
+ size_t extra,
+ sd_bus_message **ret);
+
+int bus_message_from_malloc(
+ sd_bus *bus,
+ void *buffer,
+ size_t length,
+ int *fds,
+ unsigned n_fds,
+ const struct ucred *ucred,
+ const char *label,
+ sd_bus_message **ret);
+
+const char* bus_message_get_arg(sd_bus_message *m, unsigned i);
+
+int bus_message_append_ap(sd_bus_message *m, const char *types, va_list ap);
+
+int bus_message_parse_fields(sd_bus_message *m);
+
+bool bus_header_is_complete(struct bus_header *h, size_t size);
+int bus_header_message_size(struct bus_header *h, size_t *sum);
+
+struct bus_body_part *message_append_part(sd_bus_message *m);
+
+#define MESSAGE_FOREACH_PART(part, i, m) \
+ for ((i) = 0, (part) = &(m)->body; (i) < (m)->n_body_parts; (i)++, (part) = (part)->next)
+
+int bus_body_part_map(struct bus_body_part *part);
+void bus_body_part_unmap(struct bus_body_part *part);
+
+int bus_message_to_errno(sd_bus_message *m);
+
+int bus_message_new_synthetic_error(sd_bus *bus, uint64_t serial, const sd_bus_error *e, sd_bus_message **m);
+
+int bus_message_remarshal(sd_bus *bus, sd_bus_message **m);
+
+int bus_message_append_sender(sd_bus_message *m, const char *sender);
diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c
new file mode 100644
index 0000000..b116a5d
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-objects.c
@@ -0,0 +1,2507 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/capability.h>
+
+#include "strv.h"
+#include "set.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-type.h"
+#include "bus-signature.h"
+#include "bus-introspect.h"
+#include "bus-objects.h"
+#include "bus-util.h"
+
+static int node_vtable_get_userdata(
+ sd_bus *bus,
+ const char *path,
+ struct node_vtable *c,
+ void **userdata,
+ sd_bus_error *error) {
+
+ void *u;
+ int r;
+
+ assert(bus);
+ assert(path);
+ assert(c);
+
+ u = c->userdata;
+ if (c->find) {
+ r = c->find(bus, path, c->interface, u, &u, error);
+ if (r < 0)
+ return r;
+ if (sd_bus_error_is_set(error))
+ return -sd_bus_error_get_errno(error);
+ if (r == 0)
+ return r;
+ }
+
+ if (userdata)
+ *userdata = u;
+
+ return 1;
+}
+
+static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
+ assert(p);
+
+ return (uint8_t*) u + p->x.property.offset;
+}
+
+static int vtable_property_get_userdata(
+ sd_bus *bus,
+ const char *path,
+ struct vtable_member *p,
+ void **userdata,
+ sd_bus_error *error) {
+
+ void *u;
+ int r;
+
+ assert(bus);
+ assert(path);
+ assert(p);
+ assert(userdata);
+
+ r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
+ if (r <= 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+
+ *userdata = vtable_property_convert_userdata(p->vtable, u);
+ return 1;
+}
+
+static int add_enumerated_to_set(
+ sd_bus *bus,
+ const char *prefix,
+ struct node_enumerator *first,
+ Set *s,
+ sd_bus_error *error) {
+
+ struct node_enumerator *c;
+ int r;
+
+ assert(bus);
+ assert(prefix);
+ assert(s);
+
+ LIST_FOREACH(enumerators, c, first) {
+ char **children = NULL, **k;
+
+ if (bus->nodes_modified)
+ return 0;
+
+ r = c->callback(bus, prefix, c->userdata, &children, error);
+ if (r < 0)
+ return r;
+ if (sd_bus_error_is_set(error))
+ return -sd_bus_error_get_errno(error);
+
+ STRV_FOREACH(k, children) {
+ if (r < 0) {
+ free(*k);
+ continue;
+ }
+
+ if (!object_path_is_valid(*k)){
+ free(*k);
+ r = -EINVAL;
+ continue;
+ }
+
+ if (!object_path_startswith(*k, prefix)) {
+ free(*k);
+ continue;
+ }
+
+ r = set_consume(s, *k);
+ if (r == -EEXIST)
+ r = 0;
+ }
+
+ free(children);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int add_subtree_to_set(
+ sd_bus *bus,
+ const char *prefix,
+ struct node *n,
+ Set *s,
+ sd_bus_error *error) {
+
+ struct node *i;
+ int r;
+
+ assert(bus);
+ assert(prefix);
+ assert(n);
+ assert(s);
+
+ r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+
+ LIST_FOREACH(siblings, i, n->child) {
+ char *t;
+
+ if (!object_path_startswith(i->path, prefix))
+ continue;
+
+ t = strdup(i->path);
+ if (!t)
+ return -ENOMEM;
+
+ r = set_consume(s, t);
+ if (r < 0 && r != -EEXIST)
+ return r;
+
+ r = add_subtree_to_set(bus, prefix, i, s, error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ }
+
+ return 0;
+}
+
+static int get_child_nodes(
+ sd_bus *bus,
+ const char *prefix,
+ struct node *n,
+ Set **_s,
+ sd_bus_error *error) {
+
+ Set *s = NULL;
+ int r;
+
+ assert(bus);
+ assert(prefix);
+ assert(n);
+ assert(_s);
+
+ s = set_new(string_hash_func, string_compare_func);
+ if (!s)
+ return -ENOMEM;
+
+ r = add_subtree_to_set(bus, prefix, n, s, error);
+ if (r < 0) {
+ set_free_free(s);
+ return r;
+ }
+
+ *_s = s;
+ return 0;
+}
+
+static int node_callbacks_run(
+ sd_bus *bus,
+ sd_bus_message *m,
+ struct node_callback *first,
+ bool require_fallback,
+ bool *found_object) {
+
+ struct node_callback *c;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(found_object);
+
+ LIST_FOREACH(callbacks, c, first) {
+ _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+
+ if (bus->nodes_modified)
+ return 0;
+
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ *found_object = true;
+
+ if (c->last_iteration == bus->iteration_counter)
+ continue;
+
+ c->last_iteration = bus->iteration_counter;
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ r = c->callback(bus, m, c->userdata, &error_buffer);
+ r = bus_maybe_reply_error(m, r, &error_buffer);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
+}
+
+#define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
+
+static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ uint64_t cap;
+ uid_t uid;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(c);
+
+ /* If the entire bus is trusted let's grant access */
+ if (bus->trusted)
+ return 0;
+
+ /* If the member is marked UNPRIVILEGED let's grant access */
+ if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
+ return 0;
+
+ /* If we are not connected to kdbus we cannot retrieve the
+ * effective capability set without race. Since we need this
+ * for a security decision we cannot use racy data, hence
+ * don't request it. */
+ if (bus->is_kernel)
+ r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
+ else
+ r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
+ if (r < 0)
+ return r;
+
+ /* Check have the caller has the requested capability
+ * set. Note that the flags value contains the capability
+ * number plus one, which we need to subtract here. We do this
+ * so that we have 0 as special value for "default
+ * capability". */
+ cap = CAPABILITY_SHIFT(c->vtable->flags);
+ if (cap == 0)
+ cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
+ if (cap == 0)
+ cap = CAP_SYS_ADMIN;
+ else
+ cap --;
+
+ r = sd_bus_creds_has_effective_cap(creds, cap);
+ if (r > 0)
+ return 1;
+
+ /* Caller has same UID as us, then let's grant access */
+ r = sd_bus_creds_get_uid(creds, &uid);
+ if (r >= 0) {
+ if (uid == getuid())
+ return 1;
+ }
+
+ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
+}
+
+static int method_callbacks_run(
+ sd_bus *bus,
+ sd_bus_message *m,
+ struct vtable_member *c,
+ bool require_fallback,
+ bool *found_object) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ const char *signature;
+ void *u;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(c);
+ assert(found_object);
+
+ if (require_fallback && !c->parent->is_fallback)
+ return 0;
+
+ r = check_access(bus, m, c, &error);
+ if (r < 0)
+ return bus_maybe_reply_error(m, r, &error);
+
+ r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
+ if (r <= 0)
+ return bus_maybe_reply_error(m, r, &error);
+ if (bus->nodes_modified)
+ return 0;
+
+ *found_object = true;
+
+ if (c->last_iteration == bus->iteration_counter)
+ return 0;
+
+ c->last_iteration = bus->iteration_counter;
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ signature = sd_bus_message_get_signature(m, true);
+ if (!signature)
+ return -EINVAL;
+
+ if (!streq(strempty(c->vtable->x.method.signature), signature))
+ return sd_bus_reply_method_errorf(
+ m,
+ SD_BUS_ERROR_INVALID_ARGS,
+ "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
+ signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
+
+ /* Keep track what the signature of the reply to this message
+ * should be, so that this can be enforced when sealing the
+ * reply. */
+ m->enforced_reply_signature = strempty(c->vtable->x.method.result);
+
+ if (c->vtable->x.method.handler) {
+ r = c->vtable->x.method.handler(bus, m, u, &error);
+ return bus_maybe_reply_error(m, r, &error);
+ }
+
+ /* If the method callback is NULL, make this a successful NOP */
+ r = sd_bus_reply_method_return(m, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int invoke_property_get(
+ sd_bus *bus,
+ const sd_bus_vtable *v,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ const void *p;
+ int r;
+
+ assert(bus);
+ assert(v);
+ assert(path);
+ assert(interface);
+ assert(property);
+ assert(reply);
+
+ if (v->x.property.get) {
+ r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
+ if (r < 0)
+ return r;
+ if (sd_bus_error_is_set(error))
+ return -sd_bus_error_get_errno(error);
+ return r;
+ }
+
+ /* Automatic handling if no callback is defined. */
+
+ if (streq(v->x.property.signature, "as"))
+ return sd_bus_message_append_strv(reply, *(char***) userdata);
+
+ assert(signature_is_single(v->x.property.signature, false));
+ assert(bus_type_is_basic(v->x.property.signature[0]));
+
+ switch (v->x.property.signature[0]) {
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_SIGNATURE:
+ p = strempty(*(char**) userdata);
+ break;
+
+ case SD_BUS_TYPE_OBJECT_PATH:
+ p = *(char**) userdata;
+ assert(p);
+ break;
+
+ default:
+ p = userdata;
+ break;
+ }
+
+ return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
+}
+
+static int invoke_property_set(
+ sd_bus *bus,
+ const sd_bus_vtable *v,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *value,
+ void *userdata,
+ sd_bus_error *error) {
+
+ int r;
+
+ assert(bus);
+ assert(v);
+ assert(path);
+ assert(interface);
+ assert(property);
+ assert(value);
+
+ if (v->x.property.set) {
+ r = v->x.property.set(bus, path, interface, property, value, userdata, error);
+ if (r < 0)
+ return r;
+ if (sd_bus_error_is_set(error))
+ return -sd_bus_error_get_errno(error);
+ return r;
+ }
+
+ /* Automatic handling if no callback is defined. */
+
+ assert(signature_is_single(v->x.property.signature, false));
+ assert(bus_type_is_basic(v->x.property.signature[0]));
+
+ switch (v->x.property.signature[0]) {
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE: {
+ const char *p;
+ char *n;
+
+ r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
+ if (r < 0)
+ return r;
+
+ n = strdup(p);
+ if (!n)
+ return -ENOMEM;
+
+ free(*(char**) userdata);
+ *(char**) userdata = n;
+
+ break;
+ }
+
+ default:
+ r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ return 1;
+}
+
+static int property_get_set_callbacks_run(
+ sd_bus *bus,
+ sd_bus_message *m,
+ struct vtable_member *c,
+ bool require_fallback,
+ bool is_get,
+ bool *found_object) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ void *u;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(c);
+ assert(found_object);
+
+ if (require_fallback && !c->parent->is_fallback)
+ return 0;
+
+ r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
+ if (r <= 0)
+ return bus_maybe_reply_error(m, r, &error);
+ if (bus->nodes_modified)
+ return 0;
+
+ *found_object = true;
+
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0)
+ return r;
+
+ if (is_get) {
+ /* Note that we do not protect against reexecution
+ * here (using the last_iteration check, see below),
+ * should the node tree have changed and we got called
+ * again. We assume that property Get() calls are
+ * ultimately without side-effects or if they aren't
+ * then at least idempotent. */
+
+ r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
+ if (r < 0)
+ return r;
+
+ /* Note that we do not do an access check here. Read
+ * access to properties is always unrestricted, since
+ * PropertiesChanged signals broadcast contents
+ * anyway. */
+
+ r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, u, &error);
+ if (r < 0)
+ return bus_maybe_reply_error(m, r, &error);
+
+ if (bus->nodes_modified)
+ return 0;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ } else {
+ if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
+ return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
+
+ /* Avoid that we call the set routine more than once
+ * if the processing of this message got restarted
+ * because the node tree changed. */
+ if (c->last_iteration == bus->iteration_counter)
+ return 0;
+
+ c->last_iteration = bus->iteration_counter;
+
+ r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
+ if (r < 0)
+ return r;
+
+ r = check_access(bus, m, c, &error);
+ if (r < 0)
+ return bus_maybe_reply_error(m, r, &error);
+
+ r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, u, &error);
+ if (r < 0)
+ return bus_maybe_reply_error(m, r, &error);
+
+ if (bus->nodes_modified)
+ return 0;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int vtable_append_one_property(
+ sd_bus *bus,
+ sd_bus_message *reply,
+ const char *path,
+ struct node_vtable *c,
+ const sd_bus_vtable *v,
+ void *userdata,
+ sd_bus_error *error) {
+
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(path);
+ assert(c);
+ assert(v);
+
+ r = sd_bus_message_open_container(reply, 'e', "sv");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", v->x.property.member);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
+ if (r < 0)
+ return r;
+
+ r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int vtable_append_all_properties(
+ sd_bus *bus,
+ sd_bus_message *reply,
+ const char *path,
+ struct node_vtable *c,
+ void *userdata,
+ sd_bus_error *error) {
+
+ const sd_bus_vtable *v;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(path);
+ assert(c);
+
+ if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
+ return 1;
+
+ for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
+ if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
+ continue;
+
+ if (v->flags & SD_BUS_VTABLE_HIDDEN)
+ continue;
+
+ r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int property_get_all_callbacks_run(
+ sd_bus *bus,
+ sd_bus_message *m,
+ struct node_vtable *first,
+ bool require_fallback,
+ const char *iface,
+ bool *found_object) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ struct node_vtable *c;
+ bool found_interface;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(found_object);
+
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "{sv}");
+ if (r < 0)
+ return r;
+
+ found_interface = !iface ||
+ streq(iface, "org.freedesktop.DBus.Properties") ||
+ streq(iface, "org.freedesktop.DBus.Peer") ||
+ streq(iface, "org.freedesktop.DBus.Introspectable");
+
+ LIST_FOREACH(vtables, c, first) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ void *u;
+
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
+ if (r < 0)
+ return bus_maybe_reply_error(m, r, &error);
+ if (bus->nodes_modified)
+ return 0;
+ if (r == 0)
+ continue;
+
+ *found_object = true;
+
+ if (iface && !streq(c->interface, iface))
+ continue;
+ found_interface = true;
+
+ r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
+ if (r < 0)
+ return bus_maybe_reply_error(m, r, &error);
+ if (bus->nodes_modified)
+ return 0;
+ }
+
+ if (!found_interface) {
+ r = sd_bus_reply_method_errorf(
+ m,
+ SD_BUS_ERROR_UNKNOWN_INTERFACE,
+ "Unknown interface '%s'.", iface);
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
+ assert(bus);
+ assert(n);
+
+ if (n->object_manager)
+ return true;
+
+ if (n->parent)
+ return bus_node_with_object_manager(bus, n->parent);
+
+ return false;
+}
+
+static bool bus_node_exists(
+ sd_bus *bus,
+ struct node *n,
+ const char *path,
+ bool require_fallback) {
+
+ struct node_vtable *c;
+ struct node_callback *k;
+
+ assert(bus);
+ assert(n);
+ assert(path);
+
+ /* Tests if there's anything attached directly to this node
+ * for the specified path */
+
+ LIST_FOREACH(callbacks, k, n->callbacks) {
+ if (require_fallback && !k->is_fallback)
+ continue;
+
+ return true;
+ }
+
+ LIST_FOREACH(vtables, c, n->vtables) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
+ return true;
+ if (bus->nodes_modified)
+ return false;
+ }
+
+ return !require_fallback && (n->enumerators || n->object_manager);
+}
+
+static int process_introspect(
+ sd_bus *bus,
+ sd_bus_message *m,
+ struct node *n,
+ bool require_fallback,
+ bool *found_object) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_set_free_free_ Set *s = NULL;
+ const char *previous_interface = NULL;
+ struct introspect intro;
+ struct node_vtable *c;
+ bool empty;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(n);
+ assert(found_object);
+
+ r = get_child_nodes(bus, m->path, n, &s, &error);
+ if (r < 0)
+ return bus_maybe_reply_error(m, r, &error);
+ if (bus->nodes_modified)
+ return 0;
+
+ r = introspect_begin(&intro, bus->trusted);
+ if (r < 0)
+ return r;
+
+ r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
+ if (r < 0)
+ return r;
+
+ empty = set_isempty(s);
+
+ LIST_FOREACH(vtables, c, n->vtables) {
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
+ if (r < 0) {
+ r = bus_maybe_reply_error(m, r, &error);
+ goto finish;
+ }
+ if (bus->nodes_modified) {
+ r = 0;
+ goto finish;
+ }
+ if (r == 0)
+ continue;
+
+ empty = false;
+
+ if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
+ continue;
+
+ if (!streq_ptr(previous_interface, c->interface)) {
+
+ if (previous_interface)
+ fputs(" </interface>\n", intro.f);
+
+ fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
+ }
+
+ r = introspect_write_interface(&intro, c->vtable);
+ if (r < 0)
+ goto finish;
+
+ previous_interface = c->interface;
+ }
+
+ if (previous_interface)
+ fputs(" </interface>\n", intro.f);
+
+ if (empty) {
+ /* Nothing?, let's see if we exist at all, and if not
+ * refuse to do anything */
+ r = bus_node_exists(bus, n, m->path, require_fallback);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ if (r == 0)
+ goto finish;
+ }
+
+ *found_object = true;
+
+ r = introspect_write_child_nodes(&intro, s, m->path);
+ if (r < 0)
+ goto finish;
+
+ r = introspect_finish(&intro, bus, m, &reply);
+ if (r < 0)
+ goto finish;
+
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0)
+ goto finish;
+
+ r = 1;
+
+finish:
+ introspect_free(&intro);
+ return r;
+}
+
+static int object_manager_serialize_path(
+ sd_bus *bus,
+ sd_bus_message *reply,
+ const char *prefix,
+ const char *path,
+ bool require_fallback,
+ sd_bus_error *error) {
+
+ const char *previous_interface = NULL;
+ bool found_something = false;
+ struct node_vtable *i;
+ struct node *n;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(prefix);
+ assert(path);
+ assert(error);
+
+ n = hashmap_get(bus->nodes, prefix);
+ if (!n)
+ return 0;
+
+ LIST_FOREACH(vtables, i, n->vtables) {
+ void *u;
+
+ if (require_fallback && !i->is_fallback)
+ continue;
+
+ r = node_vtable_get_userdata(bus, path, i, &u, error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ if (r == 0)
+ continue;
+
+ if (!found_something) {
+
+ /* Open the object part */
+
+ r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "o", path);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
+ if (r < 0)
+ return r;
+
+ found_something = true;
+ }
+
+ if (!streq_ptr(previous_interface, i->interface)) {
+
+ /* Maybe close the previous interface part */
+
+ if (previous_interface) {
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+ }
+
+ /* Open the new interface part */
+
+ r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", i->interface);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "{sv}");
+ if (r < 0)
+ return r;
+ }
+
+ r = vtable_append_all_properties(bus, reply, path, i, u, error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+
+ previous_interface = i->interface;
+ }
+
+ if (previous_interface) {
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+ }
+
+ if (found_something) {
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
+
+static int object_manager_serialize_path_and_fallbacks(
+ sd_bus *bus,
+ sd_bus_message *reply,
+ const char *path,
+ sd_bus_error *error) {
+
+ char *prefix;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(path);
+ assert(error);
+
+ /* First, add all vtables registered for this path */
+ r = object_manager_serialize_path(bus, reply, path, path, false, error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+
+ /* Second, add fallback vtables registered for any of the prefixes */
+ prefix = alloca(strlen(path) + 1);
+ OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
+ r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ }
+
+ return 0;
+}
+
+static int process_get_managed_objects(
+ sd_bus *bus,
+ sd_bus_message *m,
+ struct node *n,
+ bool require_fallback,
+ bool *found_object) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_set_free_free_ Set *s = NULL;
+ bool empty;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(n);
+ assert(found_object);
+
+ if (!bus_node_with_object_manager(bus, n))
+ return 0;
+
+ r = get_child_nodes(bus, m->path, n, &s, &error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
+ if (r < 0)
+ return r;
+
+ empty = set_isempty(s);
+ if (empty) {
+ struct node_vtable *c;
+
+ /* Hmm, so we have no children? Then let's check
+ * whether we exist at all, i.e. whether at least one
+ * vtable exists. */
+
+ LIST_FOREACH(vtables, c, n->vtables) {
+
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ empty = false;
+ break;
+ }
+
+ if (empty)
+ return 0;
+ } else {
+ Iterator i;
+ char *path;
+
+ SET_FOREACH(path, s, i) {
+ r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
+ if (r < 0)
+ return r;
+
+ if (bus->nodes_modified)
+ return 0;
+ }
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int object_find_and_run(
+ sd_bus *bus,
+ sd_bus_message *m,
+ const char *p,
+ bool require_fallback,
+ bool *found_object) {
+
+ struct node *n;
+ struct vtable_member vtable_key, *v;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(p);
+ assert(found_object);
+
+ n = hashmap_get(bus->nodes, p);
+ if (!n)
+ return 0;
+
+ /* First, try object callbacks */
+ r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
+ if (r != 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+
+ if (!m->interface || !m->member)
+ return 0;
+
+ /* Then, look for a known method */
+ vtable_key.path = (char*) p;
+ vtable_key.interface = m->interface;
+ vtable_key.member = m->member;
+
+ v = hashmap_get(bus->vtable_methods, &vtable_key);
+ if (v) {
+ r = method_callbacks_run(bus, m, v, require_fallback, found_object);
+ if (r != 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ }
+
+ /* Then, look for a known property */
+ if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
+ bool get = false;
+
+ get = streq(m->member, "Get");
+
+ if (get || streq(m->member, "Set")) {
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ vtable_key.path = (char*) p;
+
+ r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
+ if (r < 0)
+ return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
+
+ v = hashmap_get(bus->vtable_properties, &vtable_key);
+ if (v) {
+ r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
+ if (r != 0)
+ return r;
+ }
+
+ } else if (streq(m->member, "GetAll")) {
+ const char *iface;
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(m, "s", &iface);
+ if (r < 0)
+ return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
+
+ if (iface[0] == 0)
+ iface = NULL;
+
+ r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
+ if (r != 0)
+ return r;
+ }
+
+ } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+
+ if (!isempty(sd_bus_message_get_signature(m, true)))
+ return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
+
+ r = process_introspect(bus, m, n, require_fallback, found_object);
+ if (r != 0)
+ return r;
+
+ } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
+
+ if (!isempty(sd_bus_message_get_signature(m, true)))
+ return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
+
+ r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
+ if (r != 0)
+ return r;
+ }
+
+ if (bus->nodes_modified)
+ return 0;
+
+ if (!*found_object) {
+ r = bus_node_exists(bus, n, m->path, require_fallback);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ *found_object = true;
+ }
+
+ return 0;
+}
+
+int bus_process_object(sd_bus *bus, sd_bus_message *m) {
+ int r;
+ size_t pl;
+ bool found_object = false;
+
+ assert(bus);
+ assert(m);
+
+ if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
+ return 0;
+
+ if (hashmap_isempty(bus->nodes))
+ return 0;
+
+ /* Never respond to broadcast messages */
+ if (bus->bus_client && !m->destination)
+ return 0;
+
+ assert(m->path);
+ assert(m->member);
+
+ pl = strlen(m->path);
+ do {
+ char prefix[pl+1];
+
+ bus->nodes_modified = false;
+
+ r = object_find_and_run(bus, m, m->path, false, &found_object);
+ if (r != 0)
+ return r;
+
+ /* Look for fallback prefixes */
+ OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
+
+ if (bus->nodes_modified)
+ break;
+
+ r = object_find_and_run(bus, m, prefix, true, &found_object);
+ if (r != 0)
+ return r;
+ }
+
+ } while (bus->nodes_modified);
+
+ if (!found_object)
+ return 0;
+
+ if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
+ sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
+ r = sd_bus_reply_method_errorf(
+ m,
+ SD_BUS_ERROR_UNKNOWN_PROPERTY,
+ "Unknown property or interface.");
+ else
+ r = sd_bus_reply_method_errorf(
+ m,
+ SD_BUS_ERROR_UNKNOWN_METHOD,
+ "Unknown method '%s' or interface '%s'.", m->member, m->interface);
+
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
+ struct node *n, *parent;
+ const char *e;
+ _cleanup_free_ char *s = NULL;
+ char *p;
+ int r;
+
+ assert(bus);
+ assert(path);
+ assert(path[0] == '/');
+
+ n = hashmap_get(bus->nodes, path);
+ if (n)
+ return n;
+
+ r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
+ if (r < 0)
+ return NULL;
+
+ s = strdup(path);
+ if (!s)
+ return NULL;
+
+ if (streq(path, "/"))
+ parent = NULL;
+ else {
+ e = strrchr(path, '/');
+ assert(e);
+
+ p = strndupa(path, MAX(1, path - e));
+
+ parent = bus_node_allocate(bus, p);
+ if (!parent)
+ return NULL;
+ }
+
+ n = new0(struct node, 1);
+ if (!n)
+ return NULL;
+
+ n->parent = parent;
+ n->path = s;
+ s = NULL; /* do not free */
+
+ r = hashmap_put(bus->nodes, n->path, n);
+ if (r < 0) {
+ free(n->path);
+ free(n);
+ return NULL;
+ }
+
+ if (parent)
+ LIST_PREPEND(siblings, parent->child, n);
+
+ return n;
+}
+
+static void bus_node_gc(sd_bus *b, struct node *n) {
+ assert(b);
+
+ if (!n)
+ return;
+
+ if (n->child ||
+ n->callbacks ||
+ n->vtables ||
+ n->enumerators ||
+ n->object_manager)
+ return;
+
+ assert(hashmap_remove(b->nodes, n->path) == n);
+
+ if (n->parent)
+ LIST_REMOVE(siblings, n->parent->child, n);
+
+ free(n->path);
+ bus_node_gc(b, n->parent);
+ free(n);
+}
+
+static int bus_add_object(
+ sd_bus *bus,
+ bool fallback,
+ const char *path,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ struct node_callback *c;
+ struct node *n;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ n = bus_node_allocate(bus, path);
+ if (!n)
+ return -ENOMEM;
+
+ c = new0(struct node_callback, 1);
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->node = n;
+ c->callback = callback;
+ c->userdata = userdata;
+ c->is_fallback = fallback;
+
+ LIST_PREPEND(callbacks, n->callbacks, c);
+ bus->nodes_modified = true;
+
+ return 0;
+
+fail:
+ free(c);
+ bus_node_gc(bus, n);
+ return r;
+}
+
+static int bus_remove_object(
+ sd_bus *bus,
+ bool fallback,
+ const char *path,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ struct node_callback *c;
+ struct node *n;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ n = hashmap_get(bus->nodes, path);
+ if (!n)
+ return 0;
+
+ LIST_FOREACH(callbacks, c, n->callbacks)
+ if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
+ break;
+ if (!c)
+ return 0;
+
+ LIST_REMOVE(callbacks, n->callbacks, c);
+ free(c);
+
+ bus_node_gc(bus, n);
+ bus->nodes_modified = true;
+
+ return 1;
+}
+
+_public_ int sd_bus_add_object(sd_bus *bus,
+ const char *path,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ return bus_add_object(bus, false, path, callback, userdata);
+}
+
+_public_ int sd_bus_remove_object(sd_bus *bus,
+ const char *path,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ return bus_remove_object(bus, false, path, callback, userdata);
+}
+
+_public_ int sd_bus_add_fallback(sd_bus *bus,
+ const char *prefix,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ return bus_add_object(bus, true, prefix, callback, userdata);
+}
+
+_public_ int sd_bus_remove_fallback(sd_bus *bus,
+ const char *prefix,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ return bus_remove_object(bus, true, prefix, callback, userdata);
+}
+
+static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
+ assert(bus);
+
+ if (!w)
+ return;
+
+ if (w->interface && w->node && w->vtable) {
+ const sd_bus_vtable *v;
+
+ for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
+ struct vtable_member *x = NULL;
+
+ switch (v->type) {
+
+ case _SD_BUS_VTABLE_METHOD: {
+ struct vtable_member key;
+
+ key.path = w->node->path;
+ key.interface = w->interface;
+ key.member = v->x.method.member;
+
+ x = hashmap_remove(bus->vtable_methods, &key);
+ break;
+ }
+
+ case _SD_BUS_VTABLE_PROPERTY:
+ case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
+ struct vtable_member key;
+
+ key.path = w->node->path;
+ key.interface = w->interface;
+ key.member = v->x.property.member;
+ x = hashmap_remove(bus->vtable_properties, &key);
+ break;
+ }}
+
+ free(x);
+ }
+ }
+
+ free(w->interface);
+ free(w);
+}
+
+static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
+ const struct vtable_member *m = a;
+ uint8_t hash_key2[HASH_KEY_SIZE];
+ unsigned long ret;
+
+ assert(m);
+
+ ret = string_hash_func(m->path, hash_key);
+
+ /* Use a slightly different hash key for the interface */
+ memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
+ hash_key2[0]++;
+ ret ^= string_hash_func(m->interface, hash_key2);
+
+ /* And an even different one for the member */
+ hash_key2[0]++;
+ ret ^= string_hash_func(m->member, hash_key2);
+
+ return ret;
+}
+
+static int vtable_member_compare_func(const void *a, const void *b) {
+ const struct vtable_member *x = a, *y = b;
+ int r;
+
+ assert(x);
+ assert(y);
+
+ r = strcmp(x->path, y->path);
+ if (r != 0)
+ return r;
+
+ r = strcmp(x->interface, y->interface);
+ if (r != 0)
+ return r;
+
+ return strcmp(x->member, y->member);
+}
+
+static int add_object_vtable_internal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const sd_bus_vtable *vtable,
+ bool fallback,
+ sd_bus_object_find_t find,
+ void *userdata) {
+
+ struct node_vtable *c = NULL, *i, *existing = NULL;
+ const sd_bus_vtable *v;
+ struct node *n;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(interface_name_is_valid(interface), -EINVAL);
+ assert_return(vtable, -EINVAL);
+ assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
+ assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
+ !streq(interface, "org.freedesktop.DBus.Introspectable") &&
+ !streq(interface, "org.freedesktop.DBus.Peer") &&
+ !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
+
+ r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
+ if (r < 0)
+ return r;
+
+ n = bus_node_allocate(bus, path);
+ if (!n)
+ return -ENOMEM;
+
+ LIST_FOREACH(vtables, i, n->vtables) {
+ if (i->is_fallback != fallback) {
+ r = -EPROTOTYPE;
+ goto fail;
+ }
+
+ if (streq(i->interface, interface)) {
+
+ if (i->vtable == vtable) {
+ r = -EEXIST;
+ goto fail;
+ }
+
+ existing = i;
+ }
+ }
+
+ c = new0(struct node_vtable, 1);
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->node = n;
+ c->is_fallback = fallback;
+ c->vtable = vtable;
+ c->userdata = userdata;
+ c->find = find;
+
+ c->interface = strdup(interface);
+ if (!c->interface) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
+
+ switch (v->type) {
+
+ case _SD_BUS_VTABLE_METHOD: {
+ struct vtable_member *m;
+
+ if (!member_name_is_valid(v->x.method.member) ||
+ !signature_is_valid(strempty(v->x.method.signature), false) ||
+ !signature_is_valid(strempty(v->x.method.result), false) ||
+ !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
+ v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ m = new0(struct vtable_member, 1);
+ if (!m) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ m->parent = c;
+ m->path = n->path;
+ m->interface = c->interface;
+ m->member = v->x.method.member;
+ m->vtable = v;
+
+ r = hashmap_put(bus->vtable_methods, m, m);
+ if (r < 0) {
+ free(m);
+ goto fail;
+ }
+
+ break;
+ }
+
+ case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
+
+ if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ /* Fall through */
+
+ case _SD_BUS_VTABLE_PROPERTY: {
+ struct vtable_member *m;
+
+ if (!member_name_is_valid(v->x.property.member) ||
+ !signature_is_single(v->x.property.signature, false) ||
+ !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
+ v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
+ (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
+ (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ m = new0(struct vtable_member, 1);
+ if (!m) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ m->parent = c;
+ m->path = n->path;
+ m->interface = c->interface;
+ m->member = v->x.property.member;
+ m->vtable = v;
+
+ r = hashmap_put(bus->vtable_properties, m, m);
+ if (r < 0) {
+ free(m);
+ goto fail;
+ }
+
+ break;
+ }
+
+ case _SD_BUS_VTABLE_SIGNAL:
+
+ if (!member_name_is_valid(v->x.signal.member) ||
+ !signature_is_valid(strempty(v->x.signal.signature), false) ||
+ v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ break;
+
+ default:
+ r = -EINVAL;
+ goto fail;
+ }
+ }
+
+ LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
+ bus->nodes_modified = true;
+
+ return 0;
+
+fail:
+ if (c)
+ free_node_vtable(bus, c);
+
+ bus_node_gc(bus, n);
+ return r;
+}
+
+static int remove_object_vtable_internal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const sd_bus_vtable *vtable,
+ bool fallback,
+ sd_bus_object_find_t find,
+ void *userdata) {
+
+ struct node_vtable *c;
+ struct node *n;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(interface_name_is_valid(interface), -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ n = hashmap_get(bus->nodes, path);
+ if (!n)
+ return 0;
+
+ LIST_FOREACH(vtables, c, n->vtables)
+ if (streq(c->interface, interface) &&
+ c->is_fallback == fallback &&
+ c->vtable == vtable &&
+ c->find == find &&
+ c->userdata == userdata)
+ break;
+
+ if (!c)
+ return 0;
+
+ LIST_REMOVE(vtables, n->vtables, c);
+
+ free_node_vtable(bus, c);
+ bus_node_gc(bus, n);
+
+ bus->nodes_modified = true;
+
+ return 1;
+}
+
+_public_ int sd_bus_add_object_vtable(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const sd_bus_vtable *vtable,
+ void *userdata) {
+
+ return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
+}
+
+_public_ int sd_bus_remove_object_vtable(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const sd_bus_vtable *vtable,
+ void *userdata) {
+
+ return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
+}
+
+_public_ int sd_bus_add_fallback_vtable(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const sd_bus_vtable *vtable,
+ sd_bus_object_find_t find,
+ void *userdata) {
+
+ return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
+}
+
+_public_ int sd_bus_remove_fallback_vtable(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const sd_bus_vtable *vtable,
+ sd_bus_object_find_t find,
+ void *userdata) {
+
+ return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
+}
+
+_public_ int sd_bus_add_node_enumerator(
+ sd_bus *bus,
+ const char *path,
+ sd_bus_node_enumerator_t callback,
+ void *userdata) {
+
+ struct node_enumerator *c;
+ struct node *n;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ n = bus_node_allocate(bus, path);
+ if (!n)
+ return -ENOMEM;
+
+ c = new0(struct node_enumerator, 1);
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->node = n;
+ c->callback = callback;
+ c->userdata = userdata;
+
+ LIST_PREPEND(enumerators, n->enumerators, c);
+
+ bus->nodes_modified = true;
+
+ return 0;
+
+fail:
+ free(c);
+ bus_node_gc(bus, n);
+ return r;
+}
+
+_public_ int sd_bus_remove_node_enumerator(
+ sd_bus *bus,
+ const char *path,
+ sd_bus_node_enumerator_t callback,
+ void *userdata) {
+
+ struct node_enumerator *c;
+ struct node *n;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ n = hashmap_get(bus->nodes, path);
+ if (!n)
+ return 0;
+
+ LIST_FOREACH(enumerators, c, n->enumerators)
+ if (c->callback == callback && c->userdata == userdata)
+ break;
+
+ if (!c)
+ return 0;
+
+ LIST_REMOVE(enumerators, n->enumerators, c);
+ free(c);
+
+ bus_node_gc(bus, n);
+
+ bus->nodes_modified = true;
+
+ return 1;
+}
+
+static int emit_properties_changed_on_interface(
+ sd_bus *bus,
+ const char *prefix,
+ const char *path,
+ const char *interface,
+ bool require_fallback,
+ bool *found_interface,
+ char **names) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ bool has_invalidating = false, has_changing = false;
+ struct vtable_member key = {};
+ struct node_vtable *c;
+ struct node *n;
+ char **property;
+ void *u = NULL;
+ int r;
+
+ assert(bus);
+ assert(prefix);
+ assert(path);
+ assert(interface);
+ assert(found_interface);
+
+ n = hashmap_get(bus->nodes, prefix);
+ if (!n)
+ return 0;
+
+ r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "s", interface);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(m, 'a', "{sv}");
+ if (r < 0)
+ return r;
+
+ key.path = prefix;
+ key.interface = interface;
+
+ LIST_FOREACH(vtables, c, n->vtables) {
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ if (!streq(c->interface, interface))
+ continue;
+
+ r = node_vtable_get_userdata(bus, path, c, &u, &error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ if (r == 0)
+ continue;
+
+ *found_interface = true;
+
+ if (names) {
+ /* If the caller specified a list of
+ * properties we include exactly those in the
+ * PropertiesChanged message */
+
+ STRV_FOREACH(property, names) {
+ struct vtable_member *v;
+
+ assert_return(member_name_is_valid(*property), -EINVAL);
+
+ key.member = *property;
+ v = hashmap_get(bus->vtable_properties, &key);
+ if (!v)
+ return -ENOENT;
+
+ /* If there are two vtables for the same
+ * interface, let's handle this property when
+ * we come to that vtable. */
+ if (c != v->parent)
+ continue;
+
+ assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
+ v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
+
+ assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
+
+ if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
+ has_invalidating = true;
+ continue;
+ }
+
+ has_changing = true;
+
+ r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ }
+ } else {
+ const sd_bus_vtable *v;
+
+ /* If the caller specified no properties list
+ * we include all properties that are marked
+ * as changing in the message. */
+
+ for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
+ if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
+ continue;
+
+ if (v->flags & SD_BUS_VTABLE_HIDDEN)
+ continue;
+
+ if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
+ has_invalidating = true;
+ continue;
+ }
+
+ if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
+ continue;
+
+ has_changing = true;
+
+ r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ }
+ }
+ }
+
+ if (!has_invalidating && !has_changing)
+ return 0;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if (r < 0)
+ return r;
+
+ if (has_invalidating) {
+ LIST_FOREACH(vtables, c, n->vtables) {
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ if (!streq(c->interface, interface))
+ continue;
+
+ r = node_vtable_get_userdata(bus, path, c, &u, &error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ if (r == 0)
+ continue;
+
+ if (names) {
+ STRV_FOREACH(property, names) {
+ struct vtable_member *v;
+
+ key.member = *property;
+ assert_se(v = hashmap_get(bus->vtable_properties, &key));
+ assert(c == v->parent);
+
+ if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
+ continue;
+
+ r = sd_bus_message_append(m, "s", *property);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ const sd_bus_vtable *v;
+
+ for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
+ if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
+ continue;
+
+ if (v->flags & SD_BUS_VTABLE_HIDDEN)
+ continue;
+
+ if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
+ continue;
+
+ r = sd_bus_message_append(m, "s", v->x.property.member);
+ if (r < 0)
+ return r;
+ }
+ }
+ }
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_send(bus, m, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+_public_ int sd_bus_emit_properties_changed_strv(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ char **names) {
+
+ BUS_DONT_DESTROY(bus);
+ bool found_interface = false;
+ char *prefix;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(interface_name_is_valid(interface), -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+
+ /* A non-NULL but empty names list means nothing needs to be
+ generated. A NULL list OTOH indicates that all properties
+ that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
+ included in the PropertiesChanged message. */
+ if (names && names[0] == NULL)
+ return 0;
+
+ do {
+ bus->nodes_modified = false;
+
+ r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
+ if (r != 0)
+ return r;
+ if (bus->nodes_modified)
+ continue;
+
+ prefix = alloca(strlen(path) + 1);
+ OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
+ r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
+ if (r != 0)
+ return r;
+ if (bus->nodes_modified)
+ break;
+ }
+
+ } while (bus->nodes_modified);
+
+ return found_interface ? 0 : -ENOENT;
+}
+
+_public_ int sd_bus_emit_properties_changed(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *name, ...) {
+
+ char **names;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(interface_name_is_valid(interface), -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (!name)
+ return 0;
+
+ names = strv_from_stdarg_alloca(name);
+
+ return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
+}
+
+static int interfaces_added_append_one_prefix(
+ sd_bus *bus,
+ sd_bus_message *m,
+ const char *prefix,
+ const char *path,
+ const char *interface,
+ bool require_fallback) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ bool found_interface = false;
+ struct node_vtable *c;
+ struct node *n;
+ void *u = NULL;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(prefix);
+ assert(path);
+ assert(interface);
+
+ n = hashmap_get(bus->nodes, prefix);
+ if (!n)
+ return 0;
+
+ LIST_FOREACH(vtables, c, n->vtables) {
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ if (!streq(c->interface, interface))
+ continue;
+
+ r = node_vtable_get_userdata(bus, path, c, &u, &error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ if (r == 0)
+ continue;
+
+ if (!found_interface) {
+ r = sd_bus_message_append_basic(m, 's', interface);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(m, 'a', "{sv}");
+ if (r < 0)
+ return r;
+
+ found_interface = true;
+ }
+
+ r = vtable_append_all_properties(bus, m, path, c, u, &error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ }
+
+ if (found_interface) {
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+ }
+
+ return found_interface;
+}
+
+static int interfaces_added_append_one(
+ sd_bus *bus,
+ sd_bus_message *m,
+ const char *path,
+ const char *interface) {
+
+ char *prefix;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(path);
+ assert(interface);
+
+ r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
+ if (r != 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+
+ prefix = alloca(strlen(path) + 1);
+ OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
+ r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
+ if (r != 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+_public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
+ BUS_DONT_DESTROY(bus);
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ char **i;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (strv_isempty(interfaces))
+ return 0;
+
+ do {
+ bus->nodes_modified = false;
+
+ if (m)
+ m = sd_bus_message_unref(m);
+
+ r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_basic(m, 'o', path);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, interfaces) {
+ assert_return(interface_name_is_valid(*i), -EINVAL);
+
+ r = sd_bus_message_open_container(m, 'e', "sa{sv}");
+ if (r < 0)
+ return r;
+
+ r = interfaces_added_append_one(bus, m, path, *i);
+ if (r < 0)
+ return r;
+
+ if (bus->nodes_modified)
+ break;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+ }
+
+ if (bus->nodes_modified)
+ continue;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ } while (bus->nodes_modified);
+
+ return sd_bus_send(bus, m, NULL);
+}
+
+_public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
+ char **interfaces;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ interfaces = strv_from_stdarg_alloca(interface);
+
+ return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
+}
+
+_public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (strv_isempty(interfaces))
+ return 0;
+
+ r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_basic(m, 'o', path);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_strv(m, interfaces);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(bus, m, NULL);
+}
+
+_public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
+ char **interfaces;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ interfaces = strv_from_stdarg_alloca(interface);
+
+ return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
+}
+
+_public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
+ struct node *n;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ n = bus_node_allocate(bus, path);
+ if (!n)
+ return -ENOMEM;
+
+ n->object_manager = true;
+ bus->nodes_modified = true;
+ return 0;
+}
+
+_public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
+ struct node *n;
+
+ assert_return(bus, -EINVAL);
+ assert_return(object_path_is_valid(path), -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ n = hashmap_get(bus->nodes, path);
+ if (!n)
+ return 0;
+
+ if (!n->object_manager)
+ return 0;
+
+ n->object_manager = false;
+ bus->nodes_modified = true;
+ bus_node_gc(bus, n);
+
+ return 1;
+}
diff --git a/src/libsystemd/sd-bus/bus-objects.h b/src/libsystemd/sd-bus/bus-objects.h
new file mode 100644
index 0000000..420edd9
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-objects.h
@@ -0,0 +1,26 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "bus-internal.h"
+
+int bus_process_object(sd_bus *bus, sd_bus_message *m);
diff --git a/src/libsystemd/sd-bus/bus-protocol.h b/src/libsystemd/sd-bus/bus-protocol.h
new file mode 100644
index 0000000..5046d17
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-protocol.h
@@ -0,0 +1,147 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+
+/* Endianness */
+
+enum {
+ _BUS_INVALID_ENDIAN = 0,
+ BUS_LITTLE_ENDIAN = 'l',
+ BUS_BIG_ENDIAN = 'B',
+#if __BYTE_ORDER == __BIG_ENDIAN
+ BUS_NATIVE_ENDIAN = BUS_BIG_ENDIAN,
+ BUS_REVERSE_ENDIAN = BUS_LITTLE_ENDIAN
+#else
+ BUS_NATIVE_ENDIAN = BUS_LITTLE_ENDIAN,
+ BUS_REVERSE_ENDIAN = BUS_BIG_ENDIAN
+#endif
+};
+
+/* Flags */
+
+enum {
+ BUS_MESSAGE_NO_REPLY_EXPECTED = 1,
+ BUS_MESSAGE_NO_AUTO_START = 2
+};
+
+/* Header fields */
+
+enum {
+ _BUS_MESSAGE_HEADER_INVALID = 0,
+ BUS_MESSAGE_HEADER_PATH,
+ BUS_MESSAGE_HEADER_INTERFACE,
+ BUS_MESSAGE_HEADER_MEMBER,
+ BUS_MESSAGE_HEADER_ERROR_NAME,
+ BUS_MESSAGE_HEADER_REPLY_SERIAL,
+ BUS_MESSAGE_HEADER_DESTINATION,
+ BUS_MESSAGE_HEADER_SENDER,
+ BUS_MESSAGE_HEADER_SIGNATURE,
+ BUS_MESSAGE_HEADER_UNIX_FDS,
+ _BUS_MESSAGE_HEADER_MAX
+};
+
+/* RequestName parameters */
+
+enum {
+ BUS_NAME_ALLOW_REPLACEMENT = 1,
+ BUS_NAME_REPLACE_EXISTING = 2,
+ BUS_NAME_DO_NOT_QUEUE = 4
+};
+
+/* RequestName returns */
+enum {
+ BUS_NAME_PRIMARY_OWNER = 1,
+ BUS_NAME_IN_QUEUE = 2,
+ BUS_NAME_EXISTS = 3,
+ BUS_NAME_ALREADY_OWNER = 4
+};
+
+/* ReleaseName returns */
+enum {
+ BUS_NAME_RELEASED = 1,
+ BUS_NAME_NON_EXISTENT = 2,
+ BUS_NAME_NOT_OWNER = 3,
+};
+
+/* StartServiceByName returns */
+enum {
+ BUS_START_REPLY_SUCCESS = 1,
+ BUS_START_REPLY_ALREADY_RUNNING = 2,
+};
+
+#define BUS_INTROSPECT_DOCTYPE \
+ "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \
+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+
+#define BUS_INTROSPECT_INTERFACE_PEER \
+ " <interface name=\"org.freedesktop.DBus.Peer\">\n" \
+ " <method name=\"Ping\"/>\n" \
+ " <method name=\"GetMachineId\">\n" \
+ " <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " </interface>\n"
+
+#define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \
+ " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" \
+ " <method name=\"Introspect\">\n" \
+ " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " </interface>\n"
+
+#define BUS_INTROSPECT_INTERFACE_PROPERTIES \
+ " <interface name=\"org.freedesktop.DBus.Properties\">\n" \
+ " <method name=\"Get\">\n" \
+ " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
+ " <arg name=\"property\" direction=\"in\" type=\"s\"/>\n" \
+ " <arg name=\"value\" direction=\"out\" type=\"v\"/>\n" \
+ " </method>\n" \
+ " <method name=\"GetAll\">\n" \
+ " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
+ " <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>\n" \
+ " </method>\n" \
+ " <method name=\"Set\">\n" \
+ " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
+ " <arg name=\"property\" direction=\"in\" type=\"s\"/>\n" \
+ " <arg name=\"value\" direction=\"in\" type=\"v\"/>\n" \
+ " </method>\n" \
+ " <signal name=\"PropertiesChanged\">\n" \
+ " <arg type=\"s\" name=\"interface\"/>\n" \
+ " <arg type=\"a{sv}\" name=\"changed_properties\"/>\n" \
+ " <arg type=\"as\" name=\"invalidated_properties\"/>\n" \
+ " </signal>\n" \
+ " </interface>\n"
+
+#define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER \
+ " <interface name=\"org.freedesktop.DBus.ObjectManager\">\n" \
+ " <method name=\"GetManagedObjects\">\n" \
+ " <arg type=\"a{oa{sa{sv}}}\" name=\"object_paths_interfaces_and_properties\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <signal name=\"InterfacesAdded\">\n" \
+ " <arg type=\"o\" name=\"object_path\"/>\n" \
+ " <arg type=\"a{sa{sv}}\" name=\"interfaces_and_properties\"/>\n" \
+ " </signal>\n" \
+ " <signal name=\"InterfacesRemoved\">\n" \
+ " <arg type=\"o\" name=\"object_path\"/>\n" \
+ " <arg type=\"as\" name=\"interfaces\"/>\n" \
+ " </signal>\n" \
+ " </interface>\n"
diff --git a/src/libsystemd/sd-bus/bus-signature.c b/src/libsystemd/sd-bus/bus-signature.c
new file mode 100644
index 0000000..1e5bf48
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-signature.c
@@ -0,0 +1,160 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <util.h>
+
+#include "bus-signature.h"
+#include "bus-type.h"
+
+static int signature_element_length_internal(
+ const char *s,
+ bool allow_dict_entry,
+ unsigned array_depth,
+ unsigned struct_depth,
+ size_t *l) {
+
+ int r;
+
+ if (!s)
+ return -EINVAL;
+
+ assert(l);
+
+ if (bus_type_is_basic(*s) || *s == SD_BUS_TYPE_VARIANT) {
+ *l = 1;
+ return 0;
+ }
+
+ if (*s == SD_BUS_TYPE_ARRAY) {
+ size_t t;
+
+ if (array_depth >= 32)
+ return -EINVAL;
+
+ r = signature_element_length_internal(s + 1, true, array_depth+1, struct_depth, &t);
+ if (r < 0)
+ return r;
+
+ *l = t + 1;
+ return 0;
+ }
+
+ if (*s == SD_BUS_TYPE_STRUCT_BEGIN) {
+ const char *p = s + 1;
+
+ if (struct_depth >= 32)
+ return -EINVAL;
+
+ while (*p != SD_BUS_TYPE_STRUCT_END) {
+ size_t t;
+
+ r = signature_element_length_internal(p, false, array_depth, struct_depth+1, &t);
+ if (r < 0)
+ return r;
+
+ p += t;
+ }
+
+ *l = p - s + 1;
+ return 0;
+ }
+
+ if (*s == SD_BUS_TYPE_DICT_ENTRY_BEGIN && allow_dict_entry) {
+ const char *p = s + 1;
+ unsigned n = 0;
+
+ if (struct_depth >= 32)
+ return -EINVAL;
+
+ while (*p != SD_BUS_TYPE_DICT_ENTRY_END) {
+ size_t t;
+
+ if (n == 0 && !bus_type_is_basic(*p))
+ return -EINVAL;
+
+ r = signature_element_length_internal(p, false, array_depth, struct_depth+1, &t);
+ if (r < 0)
+ return r;
+
+ p += t;
+ n++;
+ }
+
+ if (n != 2)
+ return -EINVAL;
+
+ *l = p - s + 1;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+
+int signature_element_length(const char *s, size_t *l) {
+ return signature_element_length_internal(s, true, 0, 0, l);
+}
+
+bool signature_is_single(const char *s, bool allow_dict_entry) {
+ int r;
+ size_t t;
+
+ if (!s)
+ return false;
+
+ r = signature_element_length_internal(s, allow_dict_entry, 0, 0, &t);
+ if (r < 0)
+ return false;
+
+ return s[t] == 0;
+}
+
+bool signature_is_pair(const char *s) {
+
+ if (!s)
+ return false;
+
+ if (!bus_type_is_basic(*s))
+ return false;
+
+ return signature_is_single(s + 1, false);
+}
+
+bool signature_is_valid(const char *s, bool allow_dict_entry) {
+ const char *p;
+ int r;
+
+ if (!s)
+ return false;
+
+ p = s;
+ while (*p) {
+ size_t t;
+
+ r = signature_element_length_internal(p, allow_dict_entry, 0, 0, &t);
+ if (r < 0)
+ return false;
+
+ p += t;
+ }
+
+ return p - s <= 255;
+}
diff --git a/src/libsystemd/sd-bus/bus-signature.h b/src/libsystemd/sd-bus/bus-signature.h
new file mode 100644
index 0000000..2e06e30
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-signature.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+bool signature_is_single(const char *s, bool allow_dict_entry);
+bool signature_is_pair(const char *s);
+bool signature_is_valid(const char *s, bool allow_dict_entry);
+
+int signature_element_length(const char *s, size_t *l);
diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
new file mode 100644
index 0000000..0c4b6af
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-socket.c
@@ -0,0 +1,1105 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <endian.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <byteswap.h>
+
+#include "util.h"
+#include "macro.h"
+#include "missing.h"
+#include "strv.h"
+#include "utf8.h"
+#include "sd-daemon.h"
+
+#include "sd-bus.h"
+#include "bus-socket.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+
+#define SNDBUF_SIZE (8*1024*1024)
+
+static void iovec_advance(struct iovec iov[], unsigned *idx, size_t size) {
+
+ while (size > 0) {
+ struct iovec *i = iov + *idx;
+
+ if (i->iov_len > size) {
+ i->iov_base = (uint8_t*) i->iov_base + size;
+ i->iov_len -= size;
+ return;
+ }
+
+ size -= i->iov_len;
+
+ i->iov_base = NULL;
+ i->iov_len = 0;
+
+ (*idx) ++;
+ }
+}
+
+static int append_iovec(sd_bus_message *m, const void *p, size_t sz) {
+ assert(m);
+ assert(p);
+ assert(sz > 0);
+
+ m->iovec[m->n_iovec].iov_base = (void*) p;
+ m->iovec[m->n_iovec].iov_len = sz;
+ m->n_iovec++;
+
+ return 0;
+}
+
+static int bus_message_setup_iovec(sd_bus_message *m) {
+ struct bus_body_part *part;
+ unsigned n, i;
+ int r;
+
+ assert(m);
+ assert(m->sealed);
+
+ if (m->n_iovec > 0)
+ return 0;
+
+ assert(!m->iovec);
+
+ n = 1 + m->n_body_parts;
+ if (n < ELEMENTSOF(m->iovec_fixed))
+ m->iovec = m->iovec_fixed;
+ else {
+ m->iovec = new(struct iovec, n);
+ if (!m->iovec) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ r = append_iovec(m, m->header, BUS_MESSAGE_BODY_BEGIN(m));
+ if (r < 0)
+ goto fail;
+
+ MESSAGE_FOREACH_PART(part, i, m) {
+ r = bus_body_part_map(part);
+ if (r < 0)
+ goto fail;
+
+ r = append_iovec(m, part->data, part->size);
+ if (r < 0)
+ goto fail;
+ }
+
+ assert(n == m->n_iovec);
+
+ return 0;
+
+fail:
+ m->poisoned = true;
+ return r;
+}
+
+bool bus_socket_auth_needs_write(sd_bus *b) {
+
+ unsigned i;
+
+ if (b->auth_index >= ELEMENTSOF(b->auth_iovec))
+ return false;
+
+ for (i = b->auth_index; i < ELEMENTSOF(b->auth_iovec); i++) {
+ struct iovec *j = b->auth_iovec + i;
+
+ if (j->iov_len > 0)
+ return true;
+ }
+
+ return false;
+}
+
+static int bus_socket_write_auth(sd_bus *b) {
+ ssize_t k;
+
+ assert(b);
+ assert(b->state == BUS_AUTHENTICATING);
+
+ if (!bus_socket_auth_needs_write(b))
+ return 0;
+
+ if (b->prefer_writev)
+ k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index);
+ else {
+ struct msghdr mh;
+ zero(mh);
+
+ mh.msg_iov = b->auth_iovec + b->auth_index;
+ mh.msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index;
+
+ k = sendmsg(b->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
+ if (k < 0 && errno == ENOTSOCK) {
+ b->prefer_writev = true;
+ k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index);
+ }
+ }
+
+ if (k < 0)
+ return errno == EAGAIN ? 0 : -errno;
+
+ iovec_advance(b->auth_iovec, &b->auth_index, (size_t) k);
+ return 1;
+}
+
+static int bus_socket_auth_verify_client(sd_bus *b) {
+ char *e, *f, *start;
+ sd_id128_t peer;
+ unsigned i;
+ int r;
+
+ assert(b);
+
+ /* We expect two response lines: "OK" and possibly
+ * "AGREE_UNIX_FD" */
+
+ e = memmem(b->rbuffer, b->rbuffer_size, "\r\n", 2);
+ if (!e)
+ return 0;
+
+ if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) {
+ f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
+ if (!f)
+ return 0;
+
+ start = f + 2;
+ } else {
+ f = NULL;
+ start = e + 2;
+ }
+
+ /* Nice! We got all the lines we need. First check the OK
+ * line */
+
+ if (e - (char*) b->rbuffer != 3 + 32)
+ return -EPERM;
+
+ if (memcmp(b->rbuffer, "OK ", 3))
+ return -EPERM;
+
+ b->auth = b->anonymous_auth ? BUS_AUTH_ANONYMOUS : BUS_AUTH_EXTERNAL;
+
+ for (i = 0; i < 32; i += 2) {
+ int x, y;
+
+ x = unhexchar(((char*) b->rbuffer)[3 + i]);
+ y = unhexchar(((char*) b->rbuffer)[3 + i + 1]);
+
+ if (x < 0 || y < 0)
+ return -EINVAL;
+
+ peer.bytes[i/2] = ((uint8_t) x << 4 | (uint8_t) y);
+ }
+
+ if (!sd_id128_equal(b->server_id, SD_ID128_NULL) &&
+ !sd_id128_equal(b->server_id, peer))
+ return -EPERM;
+
+ b->server_id = peer;
+
+ /* And possibly check the second line, too */
+
+ if (f)
+ b->can_fds =
+ (f - e == sizeof("\r\nAGREE_UNIX_FD") - 1) &&
+ memcmp(e + 2, "AGREE_UNIX_FD", sizeof("AGREE_UNIX_FD") - 1) == 0;
+
+ b->rbuffer_size -= (start - (char*) b->rbuffer);
+ memmove(b->rbuffer, start, b->rbuffer_size);
+
+ r = bus_start_running(b);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static bool line_equals(const char *s, size_t m, const char *line) {
+ size_t l;
+
+ l = strlen(line);
+ if (l != m)
+ return false;
+
+ return memcmp(s, line, l) == 0;
+}
+
+static bool line_begins(const char *s, size_t m, const char *word) {
+ size_t l;
+
+ l = strlen(word);
+ if (m < l)
+ return false;
+
+ if (memcmp(s, word, l) != 0)
+ return false;
+
+ return m == l || (m > l && s[l] == ' ');
+}
+
+static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) {
+ _cleanup_free_ char *token = NULL;
+
+ if (!b->anonymous_auth)
+ return 0;
+
+ if (l <= 0)
+ return 1;
+
+ assert(p[0] == ' ');
+ p++; l--;
+
+ if (l % 2 != 0)
+ return 0;
+ token = unhexmem(p, l);
+ if (!token)
+ return -ENOMEM;
+
+ if (memchr(token, 0, l/2))
+ return 0;
+
+ return !!utf8_is_valid(token);
+}
+
+static int verify_external_token(sd_bus *b, const char *p, size_t l) {
+ _cleanup_free_ char *token = NULL;
+ uid_t u;
+ int r;
+
+ /* We don't do any real authentication here. Instead, we if
+ * the owner of this bus wanted authentication he should have
+ * checked SO_PEERCRED before even creating the bus object. */
+
+ if (!b->anonymous_auth && !b->ucred_valid)
+ return 0;
+
+ if (l <= 0)
+ return 1;
+
+ assert(p[0] == ' ');
+ p++; l--;
+
+ if (l % 2 != 0)
+ return 0;
+
+ token = unhexmem(p, l);
+ if (!token)
+ return -ENOMEM;
+
+ if (memchr(token, 0, l/2))
+ return 0;
+
+ r = parse_uid(token, &u);
+ if (r < 0)
+ return 0;
+
+ /* We ignore the passed value if anonymous authentication is
+ * on anyway. */
+ if (!b->anonymous_auth && u != b->ucred.uid)
+ return 0;
+
+ return 1;
+}
+
+static int bus_socket_auth_write(sd_bus *b, const char *t) {
+ char *p;
+ size_t l;
+
+ assert(b);
+ assert(t);
+
+ /* We only make use of the first iovec */
+ assert(b->auth_index == 0 || b->auth_index == 1);
+
+ l = strlen(t);
+ p = malloc(b->auth_iovec[0].iov_len + l);
+ if (!p)
+ return -ENOMEM;
+
+ memcpy(p, b->auth_iovec[0].iov_base, b->auth_iovec[0].iov_len);
+ memcpy(p + b->auth_iovec[0].iov_len, t, l);
+
+ b->auth_iovec[0].iov_base = p;
+ b->auth_iovec[0].iov_len += l;
+
+ free(b->auth_buffer);
+ b->auth_buffer = p;
+ b->auth_index = 0;
+ return 0;
+}
+
+static int bus_socket_auth_write_ok(sd_bus *b) {
+ char t[3 + 32 + 2 + 1];
+
+ assert(b);
+
+ snprintf(t, sizeof(t), "OK " SD_ID128_FORMAT_STR "\r\n", SD_ID128_FORMAT_VAL(b->server_id));
+ char_array_0(t);
+
+ return bus_socket_auth_write(b, t);
+}
+
+static int bus_socket_auth_verify_server(sd_bus *b) {
+ char *e;
+ const char *line;
+ size_t l;
+ bool processed = false;
+ int r;
+
+ assert(b);
+
+ if (b->rbuffer_size < 1)
+ return 0;
+
+ /* First char must be a NUL byte */
+ if (*(char*) b->rbuffer != 0)
+ return -EIO;
+
+ if (b->rbuffer_size < 3)
+ return 0;
+
+ /* Begin with the first line */
+ if (b->auth_rbegin <= 0)
+ b->auth_rbegin = 1;
+
+ for (;;) {
+ /* Check if line is complete */
+ line = (char*) b->rbuffer + b->auth_rbegin;
+ e = memmem(line, b->rbuffer_size - b->auth_rbegin, "\r\n", 2);
+ if (!e)
+ return processed;
+
+ l = e - line;
+
+ if (line_begins(line, l, "AUTH ANONYMOUS")) {
+
+ r = verify_anonymous_token(b, line + 14, l - 14);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ r = bus_socket_auth_write(b, "REJECTED\r\n");
+ else {
+ b->auth = BUS_AUTH_ANONYMOUS;
+ r = bus_socket_auth_write_ok(b);
+ }
+
+ } else if (line_begins(line, l, "AUTH EXTERNAL")) {
+
+ r = verify_external_token(b, line + 13, l - 13);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ r = bus_socket_auth_write(b, "REJECTED\r\n");
+ else {
+ b->auth = BUS_AUTH_EXTERNAL;
+ r = bus_socket_auth_write_ok(b);
+ }
+
+ } else if (line_begins(line, l, "AUTH"))
+ r = bus_socket_auth_write(b, "REJECTED EXTERNAL ANONYMOUS\r\n");
+ else if (line_equals(line, l, "CANCEL") ||
+ line_begins(line, l, "ERROR")) {
+
+ b->auth = _BUS_AUTH_INVALID;
+ r = bus_socket_auth_write(b, "REJECTED\r\n");
+
+ } else if (line_equals(line, l, "BEGIN")) {
+
+ if (b->auth == _BUS_AUTH_INVALID)
+ r = bus_socket_auth_write(b, "ERROR\r\n");
+ else {
+ /* We can't leave from the auth phase
+ * before we haven't written
+ * everything queued, so let's check
+ * that */
+
+ if (bus_socket_auth_needs_write(b))
+ return 1;
+
+ b->rbuffer_size -= (e + 2 - (char*) b->rbuffer);
+ memmove(b->rbuffer, e + 2, b->rbuffer_size);
+ return bus_start_running(b);
+ }
+
+ } else if (line_begins(line, l, "DATA")) {
+
+ if (b->auth == _BUS_AUTH_INVALID)
+ r = bus_socket_auth_write(b, "ERROR\r\n");
+ else {
+ if (b->auth == BUS_AUTH_ANONYMOUS)
+ r = verify_anonymous_token(b, line + 4, l - 4);
+ else
+ r = verify_external_token(b, line + 4, l - 4);
+
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ b->auth = _BUS_AUTH_INVALID;
+ r = bus_socket_auth_write(b, "REJECTED\r\n");
+ } else
+ r = bus_socket_auth_write_ok(b);
+ }
+ } else if (line_equals(line, l, "NEGOTIATE_UNIX_FD")) {
+ if (b->auth == _BUS_AUTH_INVALID || !(b->hello_flags & KDBUS_HELLO_ACCEPT_FD))
+ r = bus_socket_auth_write(b, "ERROR\r\n");
+ else {
+ b->can_fds = true;
+ r = bus_socket_auth_write(b, "AGREE_UNIX_FD\r\n");
+ }
+ } else
+ r = bus_socket_auth_write(b, "ERROR\r\n");
+
+ if (r < 0)
+ return r;
+
+ b->auth_rbegin = e + 2 - (char*) b->rbuffer;
+
+ processed = true;
+ }
+}
+
+static int bus_socket_auth_verify(sd_bus *b) {
+ assert(b);
+
+ if (b->is_server)
+ return bus_socket_auth_verify_server(b);
+ else
+ return bus_socket_auth_verify_client(b);
+}
+
+static int bus_socket_read_auth(sd_bus *b) {
+ struct msghdr mh;
+ struct iovec iov;
+ size_t n;
+ ssize_t k;
+ int r;
+ void *p;
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX) +
+ CMSG_SPACE(sizeof(struct ucred)) +
+ CMSG_SPACE(NAME_MAX)]; /*selinux label */
+ } control;
+ struct cmsghdr *cmsg;
+ bool handle_cmsg = false;
+
+ assert(b);
+ assert(b->state == BUS_AUTHENTICATING);
+
+ r = bus_socket_auth_verify(b);
+ if (r != 0)
+ return r;
+
+ n = MAX(256u, b->rbuffer_size * 2);
+
+ if (n > BUS_AUTH_SIZE_MAX)
+ n = BUS_AUTH_SIZE_MAX;
+
+ if (b->rbuffer_size >= n)
+ return -ENOBUFS;
+
+ p = realloc(b->rbuffer, n);
+ if (!p)
+ return -ENOMEM;
+
+ b->rbuffer = p;
+
+ zero(iov);
+ iov.iov_base = (uint8_t*) b->rbuffer + b->rbuffer_size;
+ iov.iov_len = n - b->rbuffer_size;
+
+ if (b->prefer_readv)
+ k = readv(b->input_fd, &iov, 1);
+ else {
+ zero(mh);
+ mh.msg_iov = &iov;
+ mh.msg_iovlen = 1;
+ mh.msg_control = &control;
+ mh.msg_controllen = sizeof(control);
+
+ k = recvmsg(b->input_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
+ if (k < 0 && errno == ENOTSOCK) {
+ b->prefer_readv = true;
+ k = readv(b->input_fd, &iov, 1);
+ } else
+ handle_cmsg = true;
+ }
+ if (k < 0)
+ return errno == EAGAIN ? 0 : -errno;
+ if (k == 0)
+ return -ECONNRESET;
+
+ b->rbuffer_size += k;
+
+ if (handle_cmsg) {
+ for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ int j;
+
+ /* Whut? We received fds during the auth
+ * protocol? Somebody is playing games with
+ * us. Close them all, and fail */
+ j = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+ close_many((int*) CMSG_DATA(cmsg), j);
+ return -EIO;
+
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+
+ /* Ignore bogus data, which we might
+ * get on socketpair() sockets */
+ if (((struct ucred*) CMSG_DATA(cmsg))->pid != 0) {
+ memcpy(&b->ucred, CMSG_DATA(cmsg), sizeof(struct ucred));
+ b->ucred_valid = true;
+ }
+
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_SECURITY) {
+
+ size_t l;
+
+ l = cmsg->cmsg_len - CMSG_LEN(0);
+ if (l > 0) {
+ memcpy(&b->label, CMSG_DATA(cmsg), l);
+ b->label[l] = 0;
+ }
+ }
+ }
+ }
+
+ r = bus_socket_auth_verify(b);
+ if (r != 0)
+ return r;
+
+ return 1;
+}
+
+void bus_socket_setup(sd_bus *b) {
+ int enable;
+
+ assert(b);
+
+ /* Enable SO_PASSCRED + SO_PASSEC. We try this on any
+ * socket, just in case. */
+ enable = !b->bus_client;
+ setsockopt(b->input_fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable));
+
+ enable = !b->bus_client && (b->attach_flags & KDBUS_ATTACH_SECLABEL);
+ setsockopt(b->input_fd, SOL_SOCKET, SO_PASSSEC, &enable, sizeof(enable));
+
+ /* Increase the buffers to 8 MB */
+ fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE);
+ fd_inc_sndbuf(b->output_fd, SNDBUF_SIZE);
+
+ b->is_kernel = false;
+ b->message_version = 1;
+ b->message_endian = 0;
+}
+
+static void bus_get_peercred(sd_bus *b) {
+ assert(b);
+
+ /* Get the peer for socketpair() sockets */
+ b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0;
+}
+
+static int bus_socket_start_auth_client(sd_bus *b) {
+ size_t l;
+ const char *auth_suffix, *auth_prefix;
+
+ assert(b);
+
+ if (b->anonymous_auth) {
+ auth_prefix = "\0AUTH ANONYMOUS ";
+
+ /* For ANONYMOUS auth we send some arbitrary "trace" string */
+ l = 9;
+ b->auth_buffer = hexmem("anonymous", l);
+ } else {
+ char text[20 + 1]; /* enough space for a 64bit integer plus NUL */
+
+ auth_prefix = "\0AUTH EXTERNAL ";
+
+ snprintf(text, sizeof(text), "%lu", (unsigned long) geteuid());
+ char_array_0(text);
+
+ l = strlen(text);
+ b->auth_buffer = hexmem(text, l);
+ }
+
+ if (!b->auth_buffer)
+ return -ENOMEM;
+
+ if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD)
+ auth_suffix = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n";
+ else
+ auth_suffix = "\r\nBEGIN\r\n";
+
+ b->auth_iovec[0].iov_base = (void*) auth_prefix;
+ b->auth_iovec[0].iov_len = 1 + strlen(auth_prefix + 1);
+ b->auth_iovec[1].iov_base = (void*) b->auth_buffer;
+ b->auth_iovec[1].iov_len = l * 2;
+ b->auth_iovec[2].iov_base = (void*) auth_suffix;
+ b->auth_iovec[2].iov_len = strlen(auth_suffix);
+
+ return bus_socket_write_auth(b);
+}
+
+int bus_socket_start_auth(sd_bus *b) {
+ assert(b);
+
+ bus_get_peercred(b);
+
+ b->state = BUS_AUTHENTICATING;
+ b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_DEFAULT_TIMEOUT;
+
+ if (sd_is_socket(b->input_fd, AF_UNIX, 0, 0) <= 0)
+ b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD;
+
+ if (b->output_fd != b->input_fd)
+ if (sd_is_socket(b->output_fd, AF_UNIX, 0, 0) <= 0)
+ b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD;
+
+ if (b->is_server)
+ return bus_socket_read_auth(b);
+ else
+ return bus_socket_start_auth_client(b);
+}
+
+int bus_socket_connect(sd_bus *b) {
+ int r;
+
+ assert(b);
+ assert(b->input_fd < 0);
+ assert(b->output_fd < 0);
+ assert(b->sockaddr.sa.sa_family != AF_UNSPEC);
+
+ b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (b->input_fd < 0)
+ return -errno;
+
+ b->output_fd = b->input_fd;
+
+ bus_socket_setup(b);
+
+ r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
+ if (r < 0) {
+ if (errno == EINPROGRESS)
+ return 1;
+
+ return -errno;
+ }
+
+ return bus_socket_start_auth(b);
+}
+
+int bus_socket_exec(sd_bus *b) {
+ int s[2], r;
+ pid_t pid;
+
+ assert(b);
+ assert(b->input_fd < 0);
+ assert(b->output_fd < 0);
+ assert(b->exec_path);
+
+ r = socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, s);
+ if (r < 0)
+ return -errno;
+
+ pid = fork();
+ if (pid < 0) {
+ close_pipe(s);
+ return -errno;
+ }
+ if (pid == 0) {
+ /* Child */
+
+ reset_all_signal_handlers();
+
+ close_all_fds(s+1, 1);
+
+ assert_se(dup3(s[1], STDIN_FILENO, 0) == STDIN_FILENO);
+ assert_se(dup3(s[1], STDOUT_FILENO, 0) == STDOUT_FILENO);
+
+ if (s[1] != STDIN_FILENO && s[1] != STDOUT_FILENO)
+ close_nointr_nofail(s[1]);
+
+ fd_cloexec(STDIN_FILENO, false);
+ fd_cloexec(STDOUT_FILENO, false);
+ fd_nonblock(STDIN_FILENO, false);
+ fd_nonblock(STDOUT_FILENO, false);
+
+ if (b->exec_argv)
+ execvp(b->exec_path, b->exec_argv);
+ else {
+ const char *argv[] = { b->exec_path, NULL };
+ execvp(b->exec_path, (char**) argv);
+ }
+
+ _exit(EXIT_FAILURE);
+ }
+
+ close_nointr_nofail(s[1]);
+ b->output_fd = b->input_fd = s[0];
+
+ bus_socket_setup(b);
+
+ return bus_socket_start_auth(b);
+}
+
+int bus_socket_take_fd(sd_bus *b) {
+ assert(b);
+
+ bus_socket_setup(b);
+
+ return bus_socket_start_auth(b);
+}
+
+int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
+ struct iovec *iov;
+ ssize_t k;
+ size_t n;
+ unsigned j;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(idx);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ if (*idx >= BUS_MESSAGE_SIZE(m))
+ return 0;
+
+ r = bus_message_setup_iovec(m);
+ if (r < 0)
+ return r;
+
+ n = m->n_iovec * sizeof(struct iovec);
+ iov = alloca(n);
+ memcpy(iov, m->iovec, n);
+
+ j = 0;
+ iovec_advance(iov, &j, *idx);
+
+ if (bus->prefer_writev)
+ k = writev(bus->output_fd, iov, m->n_iovec);
+ else {
+ struct msghdr mh;
+ zero(mh);
+
+ if (m->n_fds > 0) {
+ struct cmsghdr *control;
+ control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds));
+
+ mh.msg_control = control;
+ control->cmsg_level = SOL_SOCKET;
+ control->cmsg_type = SCM_RIGHTS;
+ mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds);
+ memcpy(CMSG_DATA(control), m->fds, sizeof(int) * m->n_fds);
+ }
+
+ mh.msg_iov = iov;
+ mh.msg_iovlen = m->n_iovec;
+
+ k = sendmsg(bus->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
+ if (k < 0 && errno == ENOTSOCK) {
+ bus->prefer_writev = true;
+ k = writev(bus->output_fd, iov, m->n_iovec);
+ }
+ }
+
+ if (k < 0)
+ return errno == EAGAIN ? 0 : -errno;
+
+ *idx += (size_t) k;
+ return 1;
+}
+
+static int bus_socket_read_message_need(sd_bus *bus, size_t *need) {
+ uint32_t a, b;
+ uint8_t e;
+ uint64_t sum;
+
+ assert(bus);
+ assert(need);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ if (bus->rbuffer_size < sizeof(struct bus_header)) {
+ *need = sizeof(struct bus_header) + 8;
+
+ /* Minimum message size:
+ *
+ * Header +
+ *
+ * Method Call: +2 string headers
+ * Signal: +3 string headers
+ * Method Error: +1 string headers
+ * +1 uint32 headers
+ * Method Reply: +1 uint32 headers
+ *
+ * A string header is at least 9 bytes
+ * A uint32 header is at least 8 bytes
+ *
+ * Hence the minimum message size of a valid message
+ * is header + 8 bytes */
+
+ return 0;
+ }
+
+ a = ((const uint32_t*) bus->rbuffer)[1];
+ b = ((const uint32_t*) bus->rbuffer)[3];
+
+ e = ((const uint8_t*) bus->rbuffer)[0];
+ if (e == BUS_LITTLE_ENDIAN) {
+ a = le32toh(a);
+ b = le32toh(b);
+ } else if (e == BUS_BIG_ENDIAN) {
+ a = be32toh(a);
+ b = be32toh(b);
+ } else
+ return -EBADMSG;
+
+ sum = (uint64_t) sizeof(struct bus_header) + (uint64_t) ALIGN_TO(b, 8) + (uint64_t) a;
+ if (sum >= BUS_MESSAGE_SIZE_MAX)
+ return -ENOBUFS;
+
+ *need = (size_t) sum;
+ return 0;
+}
+
+static int bus_socket_make_message(sd_bus *bus, size_t size) {
+ sd_bus_message *t;
+ void *b;
+ int r;
+
+ assert(bus);
+ assert(bus->rbuffer_size >= size);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ r = bus_rqueue_make_room(bus);
+ if (r < 0)
+ return r;
+
+ if (bus->rbuffer_size > size) {
+ b = memdup((const uint8_t*) bus->rbuffer + size,
+ bus->rbuffer_size - size);
+ if (!b)
+ return -ENOMEM;
+ } else
+ b = NULL;
+
+ r = bus_message_from_malloc(bus,
+ bus->rbuffer, size,
+ bus->fds, bus->n_fds,
+ !bus->bus_client && bus->ucred_valid ? &bus->ucred : NULL,
+ !bus->bus_client && bus->label[0] ? bus->label : NULL,
+ &t);
+ if (r < 0) {
+ free(b);
+ return r;
+ }
+
+ bus->rbuffer = b;
+ bus->rbuffer_size -= size;
+
+ bus->fds = NULL;
+ bus->n_fds = 0;
+
+ bus->rqueue[bus->rqueue_size++] = t;
+
+ return 1;
+}
+
+int bus_socket_read_message(sd_bus *bus) {
+ struct msghdr mh;
+ struct iovec iov;
+ ssize_t k;
+ size_t need;
+ int r;
+ void *b;
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX) +
+ CMSG_SPACE(sizeof(struct ucred)) +
+ CMSG_SPACE(NAME_MAX)]; /*selinux label */
+ } control;
+ struct cmsghdr *cmsg;
+ bool handle_cmsg = false;
+
+ assert(bus);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ r = bus_socket_read_message_need(bus, &need);
+ if (r < 0)
+ return r;
+
+ if (bus->rbuffer_size >= need)
+ return bus_socket_make_message(bus, need);
+
+ b = realloc(bus->rbuffer, need);
+ if (!b)
+ return -ENOMEM;
+
+ bus->rbuffer = b;
+
+ zero(iov);
+ iov.iov_base = (uint8_t*) bus->rbuffer + bus->rbuffer_size;
+ iov.iov_len = need - bus->rbuffer_size;
+
+ if (bus->prefer_readv)
+ k = readv(bus->input_fd, &iov, 1);
+ else {
+ zero(mh);
+ mh.msg_iov = &iov;
+ mh.msg_iovlen = 1;
+ mh.msg_control = &control;
+ mh.msg_controllen = sizeof(control);
+
+ k = recvmsg(bus->input_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
+ if (k < 0 && errno == ENOTSOCK) {
+ bus->prefer_readv = true;
+ k = readv(bus->input_fd, &iov, 1);
+ } else
+ handle_cmsg = true;
+ }
+ if (k < 0)
+ return errno == EAGAIN ? 0 : -errno;
+ if (k == 0)
+ return -ECONNRESET;
+
+ bus->rbuffer_size += k;
+
+ if (handle_cmsg) {
+ for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ int n, *f;
+
+ n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+
+ if (!bus->can_fds) {
+ /* Whut? We received fds but this
+ * isn't actually enabled? Close them,
+ * and fail */
+
+ close_many((int*) CMSG_DATA(cmsg), n);
+ return -EIO;
+ }
+
+ f = realloc(bus->fds, sizeof(int) + (bus->n_fds + n));
+ if (!f) {
+ close_many((int*) CMSG_DATA(cmsg), n);
+ return -ENOMEM;
+ }
+
+ memcpy(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int));
+ bus->fds = f;
+ bus->n_fds += n;
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+
+ /* Ignore bogus data, which we might
+ * get on socketpair() sockets */
+ if (((struct ucred*) CMSG_DATA(cmsg))->pid != 0) {
+ memcpy(&bus->ucred, CMSG_DATA(cmsg), sizeof(struct ucred));
+ bus->ucred_valid = true;
+ }
+
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_SECURITY) {
+
+ size_t l;
+ l = cmsg->cmsg_len - CMSG_LEN(0);
+ if (l > 0) {
+ memcpy(&bus->label, CMSG_DATA(cmsg), l);
+ bus->label[l] = 0;
+ }
+ }
+ }
+ }
+
+ r = bus_socket_read_message_need(bus, &need);
+ if (r < 0)
+ return r;
+
+ if (bus->rbuffer_size >= need)
+ return bus_socket_make_message(bus, need);
+
+ return 1;
+}
+
+int bus_socket_process_opening(sd_bus *b) {
+ int error = 0;
+ socklen_t slen = sizeof(error);
+ struct pollfd p = {
+ .fd = b->output_fd,
+ .events = POLLOUT,
+ };
+ int r;
+
+ assert(b->state == BUS_OPENING);
+
+ r = poll(&p, 1, 0);
+ if (r < 0)
+ return -errno;
+
+ if (!(p.revents & (POLLOUT|POLLERR|POLLHUP)))
+ return 0;
+
+ r = getsockopt(b->output_fd, SOL_SOCKET, SO_ERROR, &error, &slen);
+ if (r < 0)
+ b->last_connect_error = errno;
+ else if (error != 0)
+ b->last_connect_error = error;
+ else if (p.revents & (POLLERR|POLLHUP))
+ b->last_connect_error = ECONNREFUSED;
+ else
+ return bus_socket_start_auth(b);
+
+ return bus_next_address(b);
+}
+
+int bus_socket_process_authenticating(sd_bus *b) {
+ int r;
+
+ assert(b);
+ assert(b->state == BUS_AUTHENTICATING);
+
+ if (now(CLOCK_MONOTONIC) >= b->auth_timeout)
+ return -ETIMEDOUT;
+
+ r = bus_socket_write_auth(b);
+ if (r != 0)
+ return r;
+
+ return bus_socket_read_auth(b);
+}
diff --git a/src/libsystemd/sd-bus/bus-socket.h b/src/libsystemd/sd-bus/bus-socket.h
new file mode 100644
index 0000000..5a1c7d4
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-socket.h
@@ -0,0 +1,39 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+void bus_socket_setup(sd_bus *b);
+
+int bus_socket_connect(sd_bus *b);
+int bus_socket_exec(sd_bus *b);
+int bus_socket_take_fd(sd_bus *b);
+int bus_socket_start_auth(sd_bus *b);
+
+int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx);
+int bus_socket_read_message(sd_bus *bus);
+
+int bus_socket_process_opening(sd_bus *b);
+int bus_socket_process_authenticating(sd_bus *b);
+
+bool bus_socket_auth_needs_write(sd_bus *b);
diff --git a/src/libsystemd/sd-bus/bus-type.c b/src/libsystemd/sd-bus/bus-type.c
new file mode 100644
index 0000000..b7914d1
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-type.c
@@ -0,0 +1,179 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "bus-type.h"
+
+bool bus_type_is_valid(char c) {
+ static const char valid[] = {
+ SD_BUS_TYPE_BYTE,
+ SD_BUS_TYPE_BOOLEAN,
+ SD_BUS_TYPE_INT16,
+ SD_BUS_TYPE_UINT16,
+ SD_BUS_TYPE_INT32,
+ SD_BUS_TYPE_UINT32,
+ SD_BUS_TYPE_INT64,
+ SD_BUS_TYPE_UINT64,
+ SD_BUS_TYPE_DOUBLE,
+ SD_BUS_TYPE_STRING,
+ SD_BUS_TYPE_OBJECT_PATH,
+ SD_BUS_TYPE_SIGNATURE,
+ SD_BUS_TYPE_ARRAY,
+ SD_BUS_TYPE_VARIANT,
+ SD_BUS_TYPE_STRUCT,
+ SD_BUS_TYPE_DICT_ENTRY,
+ SD_BUS_TYPE_UNIX_FD
+ };
+
+ return !!memchr(valid, c, sizeof(valid));
+}
+
+bool bus_type_is_valid_in_signature(char c) {
+ static const char valid[] = {
+ SD_BUS_TYPE_BYTE,
+ SD_BUS_TYPE_BOOLEAN,
+ SD_BUS_TYPE_INT16,
+ SD_BUS_TYPE_UINT16,
+ SD_BUS_TYPE_INT32,
+ SD_BUS_TYPE_UINT32,
+ SD_BUS_TYPE_INT64,
+ SD_BUS_TYPE_UINT64,
+ SD_BUS_TYPE_DOUBLE,
+ SD_BUS_TYPE_STRING,
+ SD_BUS_TYPE_OBJECT_PATH,
+ SD_BUS_TYPE_SIGNATURE,
+ SD_BUS_TYPE_ARRAY,
+ SD_BUS_TYPE_VARIANT,
+ SD_BUS_TYPE_STRUCT_BEGIN,
+ SD_BUS_TYPE_STRUCT_END,
+ SD_BUS_TYPE_DICT_ENTRY_BEGIN,
+ SD_BUS_TYPE_DICT_ENTRY_END,
+ SD_BUS_TYPE_UNIX_FD
+ };
+
+ return !!memchr(valid, c, sizeof(valid));
+}
+
+bool bus_type_is_basic(char c) {
+ static const char valid[] = {
+ SD_BUS_TYPE_BYTE,
+ SD_BUS_TYPE_BOOLEAN,
+ SD_BUS_TYPE_INT16,
+ SD_BUS_TYPE_UINT16,
+ SD_BUS_TYPE_INT32,
+ SD_BUS_TYPE_UINT32,
+ SD_BUS_TYPE_INT64,
+ SD_BUS_TYPE_UINT64,
+ SD_BUS_TYPE_DOUBLE,
+ SD_BUS_TYPE_STRING,
+ SD_BUS_TYPE_OBJECT_PATH,
+ SD_BUS_TYPE_SIGNATURE,
+ SD_BUS_TYPE_UNIX_FD
+ };
+
+ return !!memchr(valid, c, sizeof(valid));
+}
+
+bool bus_type_is_trivial(char c) {
+ static const char valid[] = {
+ SD_BUS_TYPE_BYTE,
+ SD_BUS_TYPE_BOOLEAN,
+ SD_BUS_TYPE_INT16,
+ SD_BUS_TYPE_UINT16,
+ SD_BUS_TYPE_INT32,
+ SD_BUS_TYPE_UINT32,
+ SD_BUS_TYPE_INT64,
+ SD_BUS_TYPE_UINT64,
+ SD_BUS_TYPE_DOUBLE
+ };
+
+ return !!memchr(valid, c, sizeof(valid));
+}
+
+bool bus_type_is_container(char c) {
+ static const char valid[] = {
+ SD_BUS_TYPE_ARRAY,
+ SD_BUS_TYPE_VARIANT,
+ SD_BUS_TYPE_STRUCT,
+ SD_BUS_TYPE_DICT_ENTRY
+ };
+
+ return !!memchr(valid, c, sizeof(valid));
+}
+
+int bus_type_get_alignment(char c) {
+
+ switch (c) {
+ case SD_BUS_TYPE_BYTE:
+ case SD_BUS_TYPE_SIGNATURE:
+ case SD_BUS_TYPE_VARIANT:
+ return 1;
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ return 2;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_ARRAY:
+ case SD_BUS_TYPE_UNIX_FD:
+ return 4;
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ case SD_BUS_TYPE_STRUCT:
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN:
+ return 8;
+ }
+
+ return -EINVAL;
+}
+
+int bus_type_get_size(char c) {
+
+ switch (c) {
+ case SD_BUS_TYPE_BYTE:
+ return 1;
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16:
+ return 2;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_UNIX_FD:
+ return 4;
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ return 8;
+ }
+
+ return -EINVAL;
+}
diff --git a/src/libsystemd/sd-bus/bus-type.h b/src/libsystemd/sd-bus/bus-type.h
new file mode 100644
index 0000000..2e423bb
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-type.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "sd-bus.h"
+#include "sd-bus-protocol.h"
+
+bool bus_type_is_valid(char c) _const_;
+bool bus_type_is_valid_in_signature(char c) _const_;
+bool bus_type_is_basic(char c) _const_;
+bool bus_type_is_trivial(char c) _const_;
+bool bus_type_is_container(char c) _const_;
+
+int bus_type_get_alignment(char c) _const_;
+int bus_type_get_size(char c) _const_;
diff --git a/src/libsystemd/sd-bus/bus-util.c b/src/libsystemd/sd-bus/bus-util.c
new file mode 100644
index 0000000..a468bca
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-util.c
@@ -0,0 +1,1232 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/socket.h>
+#include <sys/capability.h>
+
+#include "util.h"
+#include "strv.h"
+#include "macro.h"
+#include "def.h"
+#include "missing.h"
+
+#include "sd-event.h"
+#include "sd-bus.h"
+#include "bus-error.h"
+#include "bus-message.h"
+#include "bus-util.h"
+#include "bus-internal.h"
+
+static int name_owner_change_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ sd_event *e = userdata;
+
+ assert(bus);
+ assert(m);
+ assert(e);
+
+ sd_event_exit(e, 0);
+ return 1;
+}
+
+int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
+ _cleanup_free_ char *match = NULL;
+ const char *unique;
+ int r;
+
+ assert(e);
+ assert(bus);
+ assert(name);
+
+ /* We unregister the name here and then wait for the
+ * NameOwnerChanged signal for this event to arrive before we
+ * quit. We do this in order to make sure that any queued
+ * requests are still processed before we really exit. */
+
+ r = sd_bus_get_unique_name(bus, &unique);
+ if (r < 0)
+ return r;
+
+ r = asprintf(&match,
+ "sender='org.freedesktop.DBus',"
+ "type='signal',"
+ "interface='org.freedesktop.DBus',"
+ "member='NameOwnerChanged',"
+ "path='/org/freedesktop/DBus',"
+ "arg0='%s',"
+ "arg1='%s',"
+ "arg2=''", name, unique);
+ if (r < 0)
+ return -ENOMEM;
+
+ r = sd_bus_add_match(bus, match, name_owner_change_callback, e);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_release_name(bus, name);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int bus_event_loop_with_idle(
+ sd_event *e,
+ sd_bus *bus,
+ const char *name,
+ usec_t timeout,
+ check_idle_t check_idle,
+ void *userdata) {
+ bool exiting = false;
+ int r, code;
+
+ assert(e);
+ assert(bus);
+ assert(name);
+
+ for (;;) {
+ bool idle;
+
+ r = sd_event_get_state(e);
+ if (r < 0)
+ return r;
+ if (r == SD_EVENT_FINISHED)
+ break;
+
+ if (check_idle)
+ idle = check_idle(userdata);
+ else
+ idle = true;
+
+ r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout);
+ if (r < 0)
+ return r;
+
+ if (r == 0 && !exiting) {
+ r = bus_async_unregister_and_exit(e, bus, name);
+ if (r < 0)
+ return r;
+
+ exiting = true;
+ }
+ }
+
+ r = sd_event_get_exit_code(e, &code);
+ if (r < 0)
+ return r;
+
+ return code;
+}
+
+int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
+ _cleanup_bus_message_unref_ sd_bus_message *rep = NULL;
+ int r, has_owner = 0;
+
+ assert(c);
+ assert(name);
+
+ r = sd_bus_call_method(c,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/dbus",
+ "org.freedesktop.DBus",
+ "NameHasOwner",
+ error,
+ &rep,
+ "s",
+ name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_basic(rep, 'b', &has_owner);
+ if (r < 0)
+ return sd_bus_error_set_errno(error, r);
+
+ return has_owner;
+}
+
+int bus_verify_polkit(
+ sd_bus *bus,
+ sd_bus_message *m,
+ const char *action,
+ bool interactive,
+ bool *_challenge,
+ sd_bus_error *e) {
+
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ uid_t uid;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(action);
+
+ r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_uid(creds, &uid);
+ if (r < 0)
+ return r;
+
+ if (uid == 0)
+ return 1;
+
+#ifdef ENABLE_POLKIT
+ else {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ int authorized = false, challenge = false;
+ const char *sender;
+
+ sender = sd_bus_message_get_sender(m);
+ if (!sender)
+ return -EBADMSG;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.PolicyKit1",
+ "/org/freedesktop/PolicyKit1/Authority",
+ "org.freedesktop.PolicyKit1.Authority",
+ "CheckAuthorization",
+ e,
+ &reply,
+ "(sa{sv})sa{ss}us",
+ "system-bus-name", 1, "name", "s", sender,
+ action,
+ 0,
+ interactive ? 1 : 0,
+ "");
+
+ if (r < 0) {
+ /* Treat no PK available as access denied */
+ if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
+ sd_bus_error_free(e);
+ return -EACCES;
+ }
+
+ return r;
+ }
+
+ r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
+ if (r < 0)
+ return r;
+
+ if (authorized)
+ return 1;
+
+ if (_challenge) {
+ *_challenge = challenge;
+ return 0;
+ }
+ }
+#endif
+
+ return -EACCES;
+}
+
+#ifdef ENABLE_POLKIT
+
+typedef struct AsyncPolkitQuery {
+ sd_bus_message *request, *reply;
+ sd_bus_message_handler_t callback;
+ void *userdata;
+ uint64_t serial;
+ Hashmap *registry;
+} AsyncPolkitQuery;
+
+static void async_polkit_query_free(sd_bus *b, AsyncPolkitQuery *q) {
+
+ if (!q)
+ return;
+
+ if (q->serial > 0 && b)
+ sd_bus_call_async_cancel(b, q->serial);
+
+ if (q->registry && q->request)
+ hashmap_remove(q->registry, q->request);
+
+ sd_bus_message_unref(q->request);
+ sd_bus_message_unref(q->reply);
+
+ free(q);
+}
+
+static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+ _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+ AsyncPolkitQuery *q = userdata;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(q);
+
+ q->reply = sd_bus_message_ref(reply);
+ q->serial = 0;
+
+ r = sd_bus_message_rewind(q->request, true);
+ if (r < 0) {
+ r = sd_bus_reply_method_errno(q->request, r, NULL);
+ goto finish;
+ }
+
+ r = q->callback(bus, q->request, q->userdata, &error_buffer);
+ r = bus_maybe_reply_error(q->request, r, &error_buffer);
+
+finish:
+ async_polkit_query_free(bus, q);
+ return r;
+}
+
+#endif
+
+int bus_verify_polkit_async(
+ sd_bus *bus,
+ Hashmap **registry,
+ sd_bus_message *m,
+ const char *action,
+ bool interactive,
+ sd_bus_error *error,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+#ifdef ENABLE_POLKIT
+ _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
+ AsyncPolkitQuery *q;
+ const char *sender;
+#endif
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ uid_t uid;
+ int r;
+
+ assert(bus);
+ assert(registry);
+ assert(m);
+ assert(action);
+
+#ifdef ENABLE_POLKIT
+ q = hashmap_get(*registry, m);
+ if (q) {
+ int authorized, challenge;
+
+ /* This is the second invocation of this function, and
+ * there's already a response from polkit, let's
+ * process it */
+ assert(q->reply);
+
+ if (sd_bus_message_is_method_error(q->reply, NULL)) {
+ const sd_bus_error *e;
+
+ /* Copy error from polkit reply */
+ e = sd_bus_message_get_error(q->reply);
+ sd_bus_error_copy(error, e);
+
+ /* Treat no PK available as access denied */
+ if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN))
+ return -EACCES;
+
+ return -sd_bus_error_get_errno(e);
+ }
+
+ r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
+ if (r >= 0)
+ r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
+
+ if (r < 0)
+ return r;
+
+ if (authorized)
+ return 1;
+
+ return -EACCES;
+ }
+#endif
+
+ r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_uid(creds, &uid);
+ if (r < 0)
+ return r;
+
+ if (uid == 0)
+ return 1;
+
+#ifdef ENABLE_POLKIT
+ sender = sd_bus_message_get_sender(m);
+ if (!sender)
+ return -EBADMSG;
+
+ r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.PolicyKit1",
+ "/org/freedesktop/PolicyKit1/Authority",
+ "org.freedesktop.PolicyKit1.Authority",
+ "CheckAuthorization",
+ &pk);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(
+ pk,
+ "(sa{sv})sa{ss}us",
+ "system-bus-name", 1, "name", "s", sender,
+ action,
+ 0,
+ interactive ? 1 : 0,
+ NULL);
+ if (r < 0)
+ return r;
+
+ q = new0(AsyncPolkitQuery, 1);
+ if (!q)
+ return -ENOMEM;
+
+ q->request = sd_bus_message_ref(m);
+ q->callback = callback;
+ q->userdata = userdata;
+
+ r = hashmap_put(*registry, m, q);
+ if (r < 0) {
+ async_polkit_query_free(bus, q);
+ return r;
+ }
+
+ q->registry = *registry;
+
+ r = sd_bus_call_async(bus, pk, async_polkit_callback, q, 0, &q->serial);
+ if (r < 0) {
+ async_polkit_query_free(bus, q);
+ return r;
+ }
+
+ return 0;
+#endif
+
+ return -EACCES;
+}
+
+void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) {
+#ifdef ENABLE_POLKIT
+ AsyncPolkitQuery *q;
+
+ while ((q = hashmap_steal_first(registry)))
+ async_polkit_query_free(bus, q);
+
+ hashmap_free(registry);
+#endif
+}
+
+int bus_check_peercred(sd_bus *c) {
+ struct ucred ucred;
+ socklen_t l;
+ int fd;
+
+ assert(c);
+
+ fd = sd_bus_get_fd(c);
+ if (fd < 0)
+ return fd;
+
+ l = sizeof(struct ucred);
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
+ return -errno;
+
+ if (l != sizeof(struct ucred))
+ return -E2BIG;
+
+ if (ucred.uid != 0 && ucred.uid != geteuid())
+ return -EPERM;
+
+ return 1;
+}
+
+int bus_open_system_systemd(sd_bus **_bus) {
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ int r;
+
+ assert(_bus);
+
+ if (geteuid() != 0)
+ return sd_bus_open_system(_bus);
+
+ /* If we are root and kdbus is not available, then let's talk
+ * directly to the system instance, instead of going via the
+ * bus */
+
+#ifdef ENABLE_KDBUS
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_set_address(bus, KERNEL_SYSTEM_BUS_PATH);
+ if (r < 0)
+ return r;
+
+ bus->bus_client = true;
+
+ r = sd_bus_start(bus);
+ if (r >= 0) {
+ *_bus = bus;
+ bus = NULL;
+ return 0;
+ }
+
+ bus = sd_bus_unref(bus);
+#endif
+
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_start(bus);
+ if (r < 0)
+ return sd_bus_open_system(_bus);
+
+ r = bus_check_peercred(bus);
+ if (r < 0)
+ return r;
+
+ *_bus = bus;
+ bus = NULL;
+
+ return 0;
+}
+
+int bus_open_user_systemd(sd_bus **_bus) {
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_free_ char *ee = NULL;
+ const char *e;
+ int r;
+
+ /* Try via kdbus first, and then directly */
+
+ assert(_bus);
+
+#ifdef ENABLE_KDBUS
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ if (asprintf(&bus->address, KERNEL_USER_BUS_FMT, (unsigned long) getuid()) < 0)
+ return -ENOMEM;
+
+ bus->bus_client = true;
+
+ r = sd_bus_start(bus);
+ if (r >= 0) {
+ *_bus = bus;
+ bus = NULL;
+ return 0;
+ }
+
+ bus = sd_bus_unref(bus);
+#endif
+
+ e = secure_getenv("XDG_RUNTIME_DIR");
+ if (!e)
+ return sd_bus_open_user(_bus);
+
+ ee = bus_address_escape(e);
+ if (!ee)
+ return -ENOMEM;
+
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ bus->address = strjoin("unix:path=", ee, "/systemd/private", NULL);
+ if (!bus->address)
+ return -ENOMEM;
+
+ r = sd_bus_start(bus);
+ if (r < 0)
+ return sd_bus_open_user(_bus);
+
+ r = bus_check_peercred(bus);
+ if (r < 0)
+ return r;
+
+ *_bus = bus;
+ bus = NULL;
+
+ return 0;
+}
+
+int bus_print_property(const char *name, sd_bus_message *property, bool all) {
+ char type;
+ const char *contents;
+ int r;
+
+ assert(name);
+ assert(property);
+
+ r = sd_bus_message_peek_type(property, &type, &contents);
+ if (r < 0)
+ return r;
+
+ switch (type) {
+
+ case SD_BUS_TYPE_STRING: {
+ const char *s;
+
+ r = sd_bus_message_read_basic(property, type, &s);
+ if (r < 0)
+ return r;
+
+ if (all || !isempty(s))
+ printf("%s=%s\n", name, s);
+
+ return 1;
+ }
+
+ case SD_BUS_TYPE_BOOLEAN: {
+ bool b;
+
+ r = sd_bus_message_read_basic(property, type, &b);
+ if (r < 0)
+ return r;
+
+ printf("%s=%s\n", name, yes_no(b));
+
+ return 1;
+ }
+
+ case SD_BUS_TYPE_UINT64: {
+ uint64_t u;
+
+ r = sd_bus_message_read_basic(property, type, &u);
+ if (r < 0)
+ return r;
+
+ /* Yes, heuristics! But we can change this check
+ * should it turn out to not be sufficient */
+
+ if (endswith(name, "Timestamp")) {
+ char timestamp[FORMAT_TIMESTAMP_MAX], *t;
+
+ t = format_timestamp(timestamp, sizeof(timestamp), u);
+ if (t || all)
+ printf("%s=%s\n", name, strempty(t));
+
+ } else if (strstr(name, "USec")) {
+ char timespan[FORMAT_TIMESPAN_MAX];
+
+ printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
+ } else
+ printf("%s=%llu\n", name, (unsigned long long) u);
+
+ return 1;
+ }
+
+ case SD_BUS_TYPE_UINT32: {
+ uint32_t u;
+
+ r = sd_bus_message_read_basic(property, type, &u);
+ if (r < 0)
+ return r;
+
+ if (strstr(name, "UMask") || strstr(name, "Mode"))
+ printf("%s=%04o\n", name, u);
+ else
+ printf("%s=%u\n", name, (unsigned) u);
+
+ return 1;
+ }
+
+ case SD_BUS_TYPE_INT32: {
+ int32_t i;
+
+ r = sd_bus_message_read_basic(property, type, &i);
+ if (r < 0)
+ return r;
+
+ printf("%s=%i\n", name, (int) i);
+ return 1;
+ }
+
+ case SD_BUS_TYPE_DOUBLE: {
+ double d;
+
+ r = sd_bus_message_read_basic(property, type, &d);
+ if (r < 0)
+ return r;
+
+ printf("%s=%g\n", name, d);
+ return 1;
+ }
+
+ case SD_BUS_TYPE_ARRAY:
+ if (streq(contents, "s")) {
+ bool first = true;
+ const char *str;
+
+ r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
+ if (r < 0)
+ return r;
+
+ while((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
+ if (first)
+ printf("%s=", name);
+
+ printf("%s%s", first ? "" : " ", str);
+
+ first = false;
+ }
+ if (r < 0)
+ return r;
+
+ if (first && all)
+ printf("%s=", name);
+ if (!first || all)
+ puts("");
+
+ r = sd_bus_message_exit_container(property);
+ if (r < 0)
+ return r;
+
+ return 1;
+
+ } else if (streq(contents, "y")) {
+ const uint8_t *u;
+ size_t n;
+
+ r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
+ if (r < 0)
+ return r;
+
+ if (all || n > 0) {
+ unsigned int i;
+
+ printf("%s=", name);
+
+ for (i = 0; i < n; i++)
+ printf("%02x", u[i]);
+
+ puts("");
+ }
+
+ return 1;
+
+ } else if (streq(contents, "u")) {
+ uint32_t *u;
+ size_t n;
+
+ r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
+ if (r < 0)
+ return r;
+
+ if (all || n > 0) {
+ unsigned int i;
+
+ printf("%s=", name);
+
+ for (i = 0; i < n; i++)
+ printf("%08x", u[i]);
+
+ puts("");
+ }
+
+ return 1;
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(bus);
+ assert(path);
+
+ r = sd_bus_call_method(bus,
+ dest,
+ path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ &error,
+ &reply,
+ "s", "");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+ const char *name;
+ const char *contents;
+
+ r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
+ if (r < 0)
+ return r;
+
+ if (!filter || strv_find(filter, name)) {
+ r = sd_bus_message_peek_type(reply, NULL, &contents);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
+ if (r < 0)
+ return r;
+
+ r = bus_print_property(name, reply, all);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (all)
+ printf("%s=[unprintable]\n", name);
+ /* skip what we didn't read */
+ r = sd_bus_message_skip(reply, contents);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_bus_message_skip(reply, "v");
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ sd_id128_t *p = userdata;
+ const void *v;
+ size_t n;
+ int r;
+
+ r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
+ if (r < 0)
+ return r;
+
+ if (n == 0)
+ *p = SD_ID128_NULL;
+ else if (n == 16)
+ memcpy((*p).bytes, v, n);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ char type;
+ int r;
+
+ r = sd_bus_message_peek_type(m, &type, NULL);
+ if (r < 0)
+ return r;
+
+ switch (type) {
+ case SD_BUS_TYPE_STRING: {
+ const char *s;
+ char *str;
+ char **p = userdata;
+
+ r = sd_bus_message_read_basic(m, type, &s);
+ if (r < 0)
+ break;
+
+ if (isempty(s))
+ break;
+
+ str = strdup(s);
+ if (!str) {
+ r = -ENOMEM;
+ break;
+ }
+ free(*p);
+ *p = str;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_ARRAY: {
+ _cleanup_strv_free_ char **l = NULL;
+ char ***p = userdata;
+
+ r = bus_message_read_strv_extend(m, &l);
+ if (r < 0)
+ break;
+
+ strv_free(*p);
+ *p = l;
+ l = NULL;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_BOOLEAN: {
+ unsigned b;
+ bool *p = userdata;
+
+ r = sd_bus_message_read_basic(m, type, &b);
+ if (r < 0)
+ break;
+
+ *p = b;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_UINT32: {
+ uint64_t u;
+ uint32_t *p = userdata;
+
+ r = sd_bus_message_read_basic(m, type, &u);
+ if (r < 0)
+ break;
+
+ *p = u;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_UINT64: {
+ uint64_t t;
+ uint64_t *p = userdata;
+
+ r = sd_bus_message_read_basic(m, type, &t);
+ if (r < 0)
+ break;
+
+ *p = t;
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return r;
+}
+
+int bus_map_all_properties(sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const struct bus_properties_map *map,
+ void *userdata) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(bus);
+ assert(destination);
+ assert(path);
+ assert(map);
+
+ r = sd_bus_call_method(
+ bus,
+ destination,
+ path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ &error,
+ &m,
+ "s", "");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+ const struct bus_properties_map *prop;
+ const char *member;
+ const char *contents;
+ void *v;
+ unsigned i;
+
+ r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
+ if (r < 0)
+ return r;
+
+ for (i = 0, prop = NULL; map[i].member; i++)
+ if (streq(map[i].member, member)) {
+ prop = &map[i];
+ break;
+ }
+
+ if (prop) {
+ r = sd_bus_message_peek_type(m, NULL, &contents);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
+ if (r < 0)
+ return r;
+
+ v = (uint8_t *)userdata + prop->offset;
+ if (map[i].set)
+ r = prop->set(bus, member, m, &error, v);
+ else
+ r = map_basic(bus, member, m, &error, v);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_bus_message_skip(m, "v");
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ }
+
+ return r;
+}
+
+int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
+ int r;
+
+ assert(transport >= 0);
+ assert(transport < _BUS_TRANSPORT_MAX);
+ assert(bus);
+
+ assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
+ assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
+
+ switch (transport) {
+
+ case BUS_TRANSPORT_LOCAL:
+ if (user)
+ r = sd_bus_default_user(bus);
+ else
+ r = sd_bus_default_system(bus);
+
+ break;
+
+ case BUS_TRANSPORT_REMOTE:
+ r = sd_bus_open_system_remote(host, bus);
+ break;
+
+ case BUS_TRANSPORT_CONTAINER:
+ r = sd_bus_open_system_container(host, bus);
+ break;
+
+ default:
+ assert_not_reached("Hmm, unknown transport type.");
+ }
+
+ return r;
+}
+
+int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
+ int r;
+
+ assert(transport >= 0);
+ assert(transport < _BUS_TRANSPORT_MAX);
+ assert(bus);
+
+ assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
+ assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
+
+ switch (transport) {
+
+ case BUS_TRANSPORT_LOCAL:
+ if (user)
+ r = bus_open_user_systemd(bus);
+ else
+ r = bus_open_system_systemd(bus);
+
+ break;
+
+ case BUS_TRANSPORT_REMOTE:
+ r = sd_bus_open_system_remote(host, bus);
+ break;
+
+ case BUS_TRANSPORT_CONTAINER:
+ r = sd_bus_open_system_container(host, bus);
+ break;
+
+ default:
+ assert_not_reached("Hmm, unknown transport type.");
+ }
+
+ return r;
+}
+
+int bus_property_get_tristate(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ int *tristate = userdata;
+
+ return sd_bus_message_append(reply, "b", *tristate > 0);
+}
+
+int bus_property_get_bool(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ int b = *(bool*) userdata;
+
+ return sd_bus_message_append_basic(reply, 'b', &b);
+}
+
+#if __SIZEOF_SIZE_T__ != 8
+int bus_property_get_size(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ uint64_t sz = *(size_t*) userdata;
+
+ return sd_bus_message_append_basic(reply, 't', &sz);
+}
+#endif
+
+#if __SIZEOF_LONG__ != 8
+int bus_property_get_long(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ int64_t l = *(long*) userdata;
+
+ return sd_bus_message_append_basic(reply, 'x', &l);
+}
+
+int bus_property_get_ulong(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ uint64_t ul = *(unsigned long*) userdata;
+
+ return sd_bus_message_append_basic(reply, 't', &ul);
+}
+#endif
+
+int bus_log_parse_error(int r) {
+ log_error("Failed to parse message: %s", strerror(-r));
+ return r;
+}
+
+int bus_log_create_error(int r) {
+ log_error("Failed to create message: %s", strerror(-r));
+ return r;
+}
+
+int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
+ assert(message);
+ assert(u);
+
+ return sd_bus_message_read(
+ message,
+ "(ssssssouso)",
+ &u->id,
+ &u->description,
+ &u->load_state,
+ &u->active_state,
+ &u->sub_state,
+ &u->following,
+ &u->unit_path,
+ &u->job_id,
+ &u->job_type,
+ &u->job_path);
+}
+
+int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
+ assert(m);
+
+ if (r < 0) {
+ if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
+ sd_bus_reply_method_errno(m, r, error);
+
+ } else if (sd_bus_error_is_set(error)) {
+ if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
+ sd_bus_reply_method_error(m, error);
+ } else
+ return r;
+
+ log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s",
+ bus_message_type_to_string(m->header->type),
+ strna(m->sender),
+ strna(m->path),
+ strna(m->interface),
+ strna(m->member),
+ strna(m->root_container.signature),
+ bus_error_message(error, r));
+
+ return 1;
+}
diff --git a/src/libsystemd/sd-bus/bus-util.h b/src/libsystemd/sd-bus/bus-util.h
new file mode 100644
index 0000000..51e1613
--- /dev/null
+++ b/src/libsystemd/sd-bus/bus-util.h
@@ -0,0 +1,180 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-event.h"
+#include "sd-bus.h"
+#include "hashmap.h"
+#include "time-util.h"
+#include "util.h"
+
+typedef enum BusTransport {
+ BUS_TRANSPORT_LOCAL,
+ BUS_TRANSPORT_REMOTE,
+ BUS_TRANSPORT_CONTAINER,
+ _BUS_TRANSPORT_MAX,
+ _BUS_TRANSPORT_INVALID = -1
+} BusTransport;
+
+typedef int (*bus_property_set_t) (sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
+
+struct bus_properties_map {
+ const char *member;
+ const char *signature;
+ bus_property_set_t set;
+ size_t offset;
+};
+
+int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
+
+int bus_map_all_properties(sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const struct bus_properties_map *map,
+ void *userdata);
+
+int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name);
+
+typedef bool (*check_idle_t)(void *userdata);
+
+int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout, check_idle_t check_idle, void *userdata);
+
+int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error);
+
+int bus_check_peercred(sd_bus *c);
+
+int bus_verify_polkit(sd_bus *bus, sd_bus_message *m, const char *action, bool interactive, bool *_challenge, sd_bus_error *e);
+
+int bus_verify_polkit_async(sd_bus *bus, Hashmap **registry, sd_bus_message *m, const char *action, bool interactive, sd_bus_error *error, sd_bus_message_handler_t callback, void *userdata);
+void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry);
+
+int bus_open_system_systemd(sd_bus **_bus);
+int bus_open_user_systemd(sd_bus **_bus);
+
+int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus);
+int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
+
+int bus_print_property(const char *name, sd_bus_message *property, bool all);
+int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all);
+
+int bus_property_get_tristate(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+
+#define bus_property_get_usec ((sd_bus_property_get_t) NULL)
+#define bus_property_set_usec ((sd_bus_property_set_t) NULL)
+
+assert_cc(sizeof(int) == sizeof(int32_t));
+#define bus_property_get_int ((sd_bus_property_get_t) NULL)
+
+assert_cc(sizeof(unsigned) == sizeof(unsigned));
+#define bus_property_get_unsigned ((sd_bus_property_get_t) NULL)
+
+/* On 64bit machines we can use the default serializer for size_t and
+ * friends, otherwise we need to cast this manually */
+#if __SIZEOF_SIZE_T__ == 8
+#define bus_property_get_size ((sd_bus_property_get_t) NULL)
+#else
+int bus_property_get_size(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+#endif
+
+#if __SIZEOF_LONG__ == 8
+#define bus_property_get_long ((sd_bus_property_get_t) NULL)
+#define bus_property_get_ulong ((sd_bus_property_get_t) NULL)
+#else
+int bus_property_get_long(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+int bus_property_get_ulong(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+#endif
+
+/* uid_t and friends on Linux 32 bit. This means we can just use the
+ * default serializer for 32bit unsigned, for serializing it, and map
+ * it to NULL here */
+assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+#define bus_property_get_uid ((sd_bus_property_get_t) NULL)
+
+assert_cc(sizeof(gid_t) == sizeof(uint32_t));
+#define bus_property_get_gid ((sd_bus_property_get_t) NULL)
+
+assert_cc(sizeof(pid_t) == sizeof(uint32_t));
+#define bus_property_get_pid ((sd_bus_property_get_t) NULL)
+
+assert_cc(sizeof(mode_t) == sizeof(uint32_t));
+#define bus_property_get_mode ((sd_bus_property_get_t) NULL)
+
+int bus_log_parse_error(int r);
+int bus_log_create_error(int r);
+
+typedef struct UnitInfo {
+ const char *id;
+ const char *description;
+ const char *load_state;
+ const char *active_state;
+ const char *sub_state;
+ const char *following;
+ const char *unit_path;
+ uint32_t job_id;
+ const char *job_type;
+ const char *job_path;
+} UnitInfo;
+
+int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus*, sd_bus_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_message*, sd_bus_message_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_creds*, sd_bus_creds_unref);
+
+#define _cleanup_bus_unref_ _cleanup_(sd_bus_unrefp)
+#define _cleanup_bus_message_unref_ _cleanup_(sd_bus_message_unrefp)
+#define _cleanup_bus_creds_unref_ _cleanup_(sd_bus_creds_unrefp)
+#define _cleanup_bus_error_free_ _cleanup_(sd_bus_error_free)
+
+#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \
+ int function(sd_bus *bus, \
+ const char *path, \
+ const char *interface, \
+ const char *property, \
+ sd_bus_message *reply, \
+ void *userdata, \
+ sd_bus_error *error) { \
+ \
+ const char *value; \
+ type *field = userdata; \
+ int r; \
+ \
+ assert(bus); \
+ assert(reply); \
+ assert(field); \
+ \
+ value = strempty(name##_to_string(*field)); \
+ \
+ r = sd_bus_message_append_basic(reply, 's', value); \
+ if (r < 0) \
+ return r; \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define BUS_PROPERTY_DUAL_TIMESTAMP(name, offset, flags) \
+ SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \
+ SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags))
+
+int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);
diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c
new file mode 100644
index 0000000..e962ba3
--- /dev/null
+++ b/src/libsystemd/sd-bus/busctl.c
@@ -0,0 +1,514 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <getopt.h>
+
+#include "strv.h"
+#include "util.h"
+#include "log.h"
+#include "build.h"
+#include "pager.h"
+
+#include "sd-bus.h"
+#include "bus-message.h"
+#include "bus-internal.h"
+#include "bus-util.h"
+#include "bus-dump.h"
+
+static bool arg_no_pager = false;
+static char *arg_address = NULL;
+static bool arg_unique = false;
+static bool arg_acquired = false;
+static bool arg_activatable = false;
+static bool arg_show_machine = false;
+static char **arg_matches = NULL;
+static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
+static char *arg_host = NULL;
+static bool arg_user = false;
+
+static void pager_open_if_enabled(void) {
+
+ /* Cache result before we open the pager */
+ if (arg_no_pager)
+ return;
+
+ pager_open(false);
+}
+
+static int list_bus_names(sd_bus *bus, char **argv) {
+ _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
+ _cleanup_free_ char **merged = NULL;
+ _cleanup_hashmap_free_ Hashmap *names = NULL;
+ char **i;
+ int r;
+ size_t max_i = 0;
+ unsigned n = 0;
+ void *v;
+ char *k;
+ Iterator iterator;
+
+ assert(bus);
+
+ r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
+ if (r < 0) {
+ log_error("Failed to list names: %s", strerror(-r));
+ return r;
+ }
+
+ pager_open_if_enabled();
+
+ names = hashmap_new(string_hash_func, string_compare_func);
+ if (!names)
+ return log_oom();
+
+ STRV_FOREACH(i, acquired) {
+ max_i = MAX(max_i, strlen(*i));
+
+ r = hashmap_put(names, *i, INT_TO_PTR(1));
+ if (r < 0) {
+ log_error("Failed to add to hashmap: %s", strerror(-r));
+ return r;
+ }
+ }
+
+ STRV_FOREACH(i, activatable) {
+ max_i = MAX(max_i, strlen(*i));
+
+ r = hashmap_put(names, *i, INT_TO_PTR(2));
+ if (r < 0 && r != -EEXIST) {
+ log_error("Failed to add to hashmap: %s", strerror(-r));
+ return r;
+ }
+ }
+
+ merged = new(char*, hashmap_size(names) + 1);
+ HASHMAP_FOREACH_KEY(v, k, names, iterator)
+ merged[n++] = k;
+
+ merged[n] = NULL;
+ strv_sort(merged);
+
+ printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s",
+ (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION");
+
+ if (arg_show_machine)
+ puts(" MACHINE");
+ else
+ putchar('\n');
+
+ STRV_FOREACH(i, merged) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ sd_id128_t mid;
+
+ if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
+ /* Activatable */
+
+ printf("%-*s", (int) max_i, *i);
+ printf(" - - - (activatable) - - ");
+ if (arg_show_machine)
+ puts(" -");
+ else
+ putchar('\n');
+ continue;
+
+ }
+
+ if (!arg_unique && (*i)[0] == ':')
+ continue;
+
+ if (!arg_acquired && (*i)[0] != ':')
+ continue;
+
+ printf("%-*s", (int) max_i, *i);
+
+ r = sd_bus_get_owner(bus, *i, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION, &creds);
+ if (r >= 0) {
+ const char *unique, *session, *unit;
+ pid_t pid;
+ uid_t uid;
+
+ r = sd_bus_creds_get_pid(creds, &pid);
+ if (r >= 0) {
+ const char *comm = NULL;
+
+ sd_bus_creds_get_comm(creds, &comm);
+
+ printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
+ } else
+ fputs(" - - ", stdout);
+
+ r = sd_bus_creds_get_uid(creds, &uid);
+ if (r >= 0) {
+ _cleanup_free_ char *u = NULL;
+
+ u = uid_to_name(uid);
+ if (!u)
+ return log_oom();
+
+ if (strlen(u) > 16)
+ u[16] = 0;
+
+ printf(" %-16s", u);
+ } else
+ fputs(" - ", stdout);
+
+ r = sd_bus_creds_get_unique_name(creds, &unique);
+ if (r >= 0)
+ printf(" %-13s", unique);
+ else
+ fputs(" - ", stdout);
+
+ r = sd_bus_creds_get_unit(creds, &unit);
+ if (r >= 0) {
+ _cleanup_free_ char *e;
+
+ e = ellipsize(unit, 25, 100);
+ if (!e)
+ return log_oom();
+
+ printf(" %-25s", e);
+ } else
+ fputs(" - ", stdout);
+
+ r = sd_bus_creds_get_session(creds, &session);
+ if (r >= 0)
+ printf(" %-10s", session);
+ else
+ fputs(" - ", stdout);
+
+ } else
+ printf(" - - - - - - ");
+
+ if (arg_show_machine) {
+ r = sd_bus_get_owner_machine_id(bus, *i, &mid);
+ if (r >= 0) {
+ char m[SD_ID128_STRING_MAX];
+ printf(" %s\n", sd_id128_to_string(mid, m));
+ } else
+ puts(" -");
+ } else
+ putchar('\n');
+ }
+
+ return 0;
+}
+
+static int monitor(sd_bus *bus, char *argv[]) {
+ bool added_something = false;
+ char **i;
+ int r;
+
+ STRV_FOREACH(i, argv+1) {
+ _cleanup_free_ char *m = NULL;
+
+ if (!service_name_is_valid(*i)) {
+ log_error("Invalid service name '%s'", *i);
+ return -EINVAL;
+ }
+
+ m = strjoin("sender='", *i, "'", NULL);
+ if (!m)
+ return log_oom();
+
+ r = sd_bus_add_match(bus, m, NULL, NULL);
+ if (r < 0) {
+ log_error("Failed to add match: %s", strerror(-r));
+ return r;
+ }
+
+ added_something = true;
+ }
+
+ STRV_FOREACH(i, arg_matches) {
+ r = sd_bus_add_match(bus, *i, NULL, NULL);
+ if (r < 0) {
+ log_error("Failed to add match: %s", strerror(-r));
+ return r;
+ }
+
+ added_something = true;
+ }
+
+ if (!added_something) {
+ r = sd_bus_add_match(bus, "", NULL, NULL);
+ if (r < 0) {
+ log_error("Failed to add match: %s", strerror(-r));
+ return r;
+ }
+ }
+
+ for (;;) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+
+ r = sd_bus_process(bus, &m);
+ if (r < 0) {
+ log_error("Failed to process bus: %s", strerror(-r));
+ return r;
+ }
+
+ if (m) {
+ bus_message_dump(m, stdout, true);
+ continue;
+ }
+
+ if (r > 0)
+ continue;
+
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0) {
+ log_error("Failed to wait for bus: %s", strerror(-r));
+ return r;
+ }
+ }
+}
+
+static int status(sd_bus *bus, char *argv[]) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ pid_t pid;
+ int r;
+
+ assert(bus);
+
+ if (strv_length(argv) != 2) {
+ log_error("Expects one argument.");
+ return -EINVAL;
+ }
+
+ r = parse_pid(argv[1], &pid);
+ if (r < 0)
+ r = sd_bus_get_owner(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
+ else
+ r = sd_bus_creds_new_from_pid(pid, _SD_BUS_CREDS_ALL, &creds);
+
+ if (r < 0) {
+ log_error("Failed to get credentials: %s", strerror(-r));
+ return r;
+ }
+
+ bus_creds_dump(creds, NULL);
+ return 0;
+}
+
+static int help(void) {
+
+ printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+ "Introspect the bus.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --system Connect to system bus\n"
+ " --user Connect to user bus\n"
+ " -H --host=[USER@]HOST Operate on remote host\n"
+ " -M --machine=CONTAINER Operate on local container\n"
+ " --address=ADDRESS Connect to bus specified by address\n"
+ " --show-machine Show machine ID column in list\n"
+ " --unique Only show unique names\n"
+ " --acquired Only show acquired names\n"
+ " --activatable Only show activatable names\n"
+ " --match=MATCH Only show matching messages\n\n"
+ "Commands:\n"
+ " list List bus names\n"
+ " monitor [SERVICE...] Show bus traffic\n"
+ " status NAME Show name status\n"
+ " help Show this help\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_NO_PAGER,
+ ARG_SYSTEM,
+ ARG_USER,
+ ARG_ADDRESS,
+ ARG_MATCH,
+ ARG_SHOW_MACHINE,
+ ARG_UNIQUE,
+ ARG_ACQUIRED,
+ ARG_ACTIVATABLE
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "system", no_argument, NULL, ARG_SYSTEM },
+ { "user", no_argument, NULL, ARG_USER },
+ { "address", required_argument, NULL, ARG_ADDRESS },
+ { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
+ { "unique", no_argument, NULL, ARG_UNIQUE },
+ { "acquired", no_argument, NULL, ARG_ACQUIRED },
+ { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
+ { "match", required_argument, NULL, ARG_MATCH },
+ { "host", required_argument, NULL, 'H' },
+ { "machine", required_argument, NULL, 'M' },
+ {},
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ return help();
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case ARG_NO_PAGER:
+ arg_no_pager = true;
+ break;
+
+ case ARG_USER:
+ arg_user = true;
+ break;
+
+ case ARG_SYSTEM:
+ arg_user = false;
+ break;
+
+ case ARG_ADDRESS:
+ arg_address = optarg;
+ break;
+
+ case ARG_SHOW_MACHINE:
+ arg_show_machine = true;
+ break;
+
+ case ARG_UNIQUE:
+ arg_unique = true;
+ break;
+
+ case ARG_ACQUIRED:
+ arg_acquired = true;
+ break;
+
+ case ARG_ACTIVATABLE:
+ arg_activatable = true;
+ break;
+
+ case ARG_MATCH:
+ if (strv_extend(&arg_matches, optarg) < 0)
+ return log_oom();
+ break;
+
+ case 'H':
+ arg_transport = BUS_TRANSPORT_REMOTE;
+ arg_host = optarg;
+ break;
+
+ case 'M':
+ arg_transport = BUS_TRANSPORT_CONTAINER;
+ arg_host = optarg;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+ }
+
+ if (!arg_unique && !arg_acquired && !arg_activatable)
+ arg_unique = arg_acquired = arg_activatable = true;
+
+ return 1;
+}
+
+static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
+ assert(bus);
+
+ if (optind >= argc ||
+ streq(argv[optind], "list"))
+ return list_bus_names(bus, argv + optind);
+
+ if (streq(argv[optind], "monitor"))
+ return monitor(bus, argv + optind);
+
+ if (streq(argv[optind], "status"))
+ return status(bus, argv + optind);
+
+ if (streq(argv[optind], "help"))
+ return help();
+
+ log_error("Unknown command '%s'", argv[optind]);
+ return -EINVAL;
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ int r;
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finish;
+
+ if (arg_address) {
+ r = sd_bus_new(&bus);
+ if (r < 0) {
+ log_error("Failed to allocate bus: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_set_address(bus, arg_address);
+ if (r < 0) {
+ log_error("Failed to set address: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_set_bus_client(bus, true);
+ if (r < 0) {
+ log_error("Failed to set bus client: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_start(bus);
+ } else
+ r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
+
+ if (r < 0) {
+ log_error("Failed to connect to bus: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = busctl_main(bus, argc, argv);
+
+finish:
+ pager_close();
+
+ strv_free(arg_matches);
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/libsystemd/sd-bus/kdbus.h b/src/libsystemd/sd-bus/kdbus.h
new file mode 100644
index 0000000..fe0bb48
--- /dev/null
+++ b/src/libsystemd/sd-bus/kdbus.h
@@ -0,0 +1,900 @@
+/*
+ * Copyright (C) 2013 Kay Sievers
+ * Copyright (C) 2013 Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+ * Copyright (C) 2013 Linux Foundation
+ * Copyright (C) 2013 Lennart Poettering
+ * Copyright (C) 2013 Daniel Mack <daniel at zonque.org>
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * "Everything should be made as simple as possible, but not simpler."
+ * -- Albert Einstein
+ */
+
+#ifndef _KDBUS_H_
+#define _KDBUS_H_
+
+#ifndef __KERNEL__
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <linux/types.h>
+#endif
+
+#define KDBUS_IOC_MAGIC 0x95
+#define KDBUS_SRC_ID_KERNEL (0)
+#define KDBUS_DST_ID_NAME (0)
+#define KDBUS_MATCH_ID_ANY (~0ULL)
+#define KDBUS_DST_ID_BROADCAST (~0ULL)
+
+/**
+ * struct kdbus_notify_id_change - name registry change message
+ * @id: New or former owner of the name
+ * @flags: flags field from KDBUS_HELLO_*
+ *
+ * Sent from kernel to userspace when the owner or activator of
+ * a well-known name changes.
+ *
+ * Attached to:
+ * KDBUS_ITEM_ID_ADD
+ * KDBUS_ITEM_ID_REMOVE
+ */
+struct kdbus_notify_id_change {
+ __u64 id;
+ __u64 flags;
+};
+
+/**
+ * struct kdbus_notify_name_change - name registry change message
+ * @old: ID and flags of former owner of a name
+ * @new: ID and flags of new owner of a name
+ * @name: Well-known name
+ *
+ * Sent from kernel to userspace when the owner or activator of
+ * a well-known name changes.
+ *
+ * Attached to:
+ * KDBUS_ITEM_NAME_ADD
+ * KDBUS_ITEM_NAME_REMOVE
+ * KDBUS_ITEM_NAME_CHANGE
+ */
+struct kdbus_notify_name_change {
+ struct kdbus_notify_id_change old;
+ struct kdbus_notify_id_change new;
+ char name[0];
+};
+
+/**
+ * struct kdbus_creds - process credentials
+ * @uid: User ID
+ * @gid: Group ID
+ * @pid: Process ID
+ * @tid: Thread ID
+ * @starttime: Starttime of the process
+ *
+ * The starttime of the process PID. This is useful to detect PID overruns
+ * from the client side. i.e. if you use the PID to look something up in
+ * /proc/$PID/ you can afterwards check the starttime field of it, to ensure
+ * you didn't run into a PID overrun.
+ *
+ * Attached to:
+ * KDBUS_ITEM_CREDS
+ */
+struct kdbus_creds {
+ __u64 uid;
+ __u64 gid;
+ __u64 pid;
+ __u64 tid;
+ __u64 starttime;
+};
+
+/**
+ * struct kdbus_audit - audit information
+ * @sessionid: The audit session ID
+ * @loginuid: The audit login uid
+ *
+ * Attached to:
+ * KDBUS_ITEM_AUDIT
+ */
+struct kdbus_audit {
+ __u64 sessionid;
+ __u64 loginuid;
+};
+
+/**
+ * struct kdbus_timestamp
+ * @seqnum: Global per-namespace message sequence number
+ * @monotonic_ns: Monotonic timestamp, in nanoseconds
+ * @realtime_ns: Realtime timestamp, in nanoseconds
+ *
+ * Attached to:
+ * KDBUS_ITEM_TIMESTAMP
+ */
+struct kdbus_timestamp {
+ __u64 seqnum;
+ __u64 monotonic_ns;
+ __u64 realtime_ns;
+};
+
+/**
+ * struct kdbus_vec - I/O vector for kdbus payload items
+ * @size: The size of the vector
+ * @address: Memory address for memory addresses
+ * @offset: Offset in the in-message payload memory,
+ * relative to the message head
+ *
+ * Attached to:
+ * KDBUS_ITEM_PAYLOAD_VEC
+ */
+struct kdbus_vec {
+ __u64 size;
+ union {
+ __u64 address;
+ __u64 offset;
+ };
+};
+
+/**
+ * struct kdbus_memfd - a kdbus memfd
+ * @size: The memfd's size
+ * @fd: The file descriptor number
+ * @__pad: Padding to ensure proper alignement and size
+ *
+ * Attached to:
+ * KDBUS_ITEM_PAYLOAD_MEMFD
+ */
+struct kdbus_memfd {
+ __u64 size;
+ int fd;
+ __u32 __pad;
+};
+
+/**
+ * struct kdbus_name - a registered well-known name with its flags
+ * @flags: flags from KDBUS_NAME_*
+ * @name: well-known name
+ *
+ * Attached to:
+ * KDBUS_ITEM_NAME
+ */
+struct kdbus_name {
+ __u64 flags;
+ char name[0];
+};
+
+/**
+ * struct kdbus_policy_access - policy access item
+ * @type: One of KDBUS_POLICY_ACCESS_* types
+ * @bits: Access to grant. One of KDBUS_POLICY_*
+ * @id: For KDBUS_POLICY_ACCESS_USER, the uid
+ * For KDBUS_POLICY_ACCESS_GROUP, the gid
+ *
+ * Embedded in:
+ * struct kdbus_policy
+ */
+struct kdbus_policy_access {
+ __u64 type; /* USER, GROUP, WORLD */
+ __u64 bits; /* RECV, SEND, OWN */
+ __u64 id; /* uid, gid, 0 */
+};
+
+/**
+ * struct kdbus_policy - a policy item
+ * @access: Policy access details
+ * @name: Well-known name to grant access to
+ *
+ * Attached to:
+ * KDBUS_POLICY_ACCESS
+ * KDBUS_ITEM_POLICY_NAME
+ */
+struct kdbus_policy {
+ union {
+ struct kdbus_policy_access access;
+ char name[0];
+ };
+};
+
+/**
+ * enum kdbus_item_type - item types to chain data in a list
+ * @_KDBUS_ITEM_NULL: Uninitialized/invalid
+ * @_KDBUS_ITEM_USER_BASE: Start of user items
+ * @KDBUS_ITEM_PAYLOAD_VEC: Vector to data
+ * @KDBUS_ITEM_PAYLOAD_OFF: Data at returned offset to message head
+ * @KDBUS_ITEM_PAYLOAD_MEMFD: Data as sealed memfd
+ * @KDBUS_ITEM_FDS: Attached file descriptors
+ * @KDBUS_ITEM_BLOOM: For broadcasts, carries bloom filter
+ * @KDBUS_ITEM_BLOOM_SIZE: Desired bloom size, used by KDBUS_CMD_BUS_MAKE
+ * @KDBUS_ITEM_DST_NAME: Destination's well-known name
+ * @KDBUS_ITEM_MAKE_NAME: Name of namespace, bus, endpoint
+ * @KDBUS_ITEM_MEMFD_NAME: The human readable name of a memfd (debugging)
+ * @_KDBUS_ITEM_POLICY_BASE: Start of policy items
+ * @KDBUS_ITEM_POLICY_NAME: Policy in struct kdbus_policy
+ * @KDBUS_ITEM_POLICY_ACCESS: Policy in struct kdbus_policy
+ * @_KDBUS_ITEM_ATTACH_BASE: Start of metadata attach items
+ * @KDBUS_ITEM_NAME: Well-know name with flags
+ * @KDBUS_ITEM_ID: Connection ID
+ * @KDBUS_ITEM_TIMESTAMP: Timestamp
+ * @KDBUS_ITEM_CREDS: Process credential
+ * @KDBUS_ITEM_PID_COMM: Process ID "comm" identifier
+ * @KDBUS_ITEM_TID_COMM: Thread ID "comm" identifier
+ * @KDBUS_ITEM_EXE: The path of the executable
+ * @KDBUS_ITEM_CMDLINE: The process command line
+ * @KDBUS_ITEM_CGROUP: The croup membership
+ * @KDBUS_ITEM_CAPS: The process capabilities
+ * @KDBUS_ITEM_SECLABEL: The security label
+ * @KDBUS_ITEM_AUDIT: The audit IDs
+ * @KDBUS_ITEM_CONN_NAME: The connection's human-readable name (debugging)
+ * @_KDBUS_ITEM_KERNEL_BASE: Start of kernel-generated message items
+ * @KDBUS_ITEM_NAME_ADD: Notify in struct kdbus_notify_name_change
+ * @KDBUS_ITEM_NAME_REMOVE: Notify in struct kdbus_notify_name_change
+ * @KDBUS_ITEM_NAME_CHANGE: Notify in struct kdbus_notify_name_change
+ * @KDBUS_ITEM_ID_ADD: Notify in struct kdbus_notify_id_change
+ * @KDBUS_ITEM_ID_REMOVE: Notify in struct kdbus_notify_id_change
+ * @KDBUS_ITEM_REPLY_TIMEOUT: Timeout has been reached
+ * @KDBUS_ITEM_REPLY_DEAD: Destination died
+ */
+enum kdbus_item_type {
+ _KDBUS_ITEM_NULL,
+ _KDBUS_ITEM_USER_BASE,
+ KDBUS_ITEM_PAYLOAD_VEC = _KDBUS_ITEM_USER_BASE,
+ KDBUS_ITEM_PAYLOAD_OFF,
+ KDBUS_ITEM_PAYLOAD_MEMFD,
+ KDBUS_ITEM_FDS,
+ KDBUS_ITEM_BLOOM,
+ KDBUS_ITEM_BLOOM_SIZE,
+ KDBUS_ITEM_DST_NAME,
+ KDBUS_ITEM_MAKE_NAME,
+ KDBUS_ITEM_MEMFD_NAME,
+
+ _KDBUS_ITEM_POLICY_BASE = 0x1000,
+ KDBUS_ITEM_POLICY_NAME = _KDBUS_ITEM_POLICY_BASE,
+ KDBUS_ITEM_POLICY_ACCESS,
+
+ _KDBUS_ITEM_ATTACH_BASE = 0x2000,
+ KDBUS_ITEM_NAME = _KDBUS_ITEM_ATTACH_BASE,
+ KDBUS_ITEM_ID,
+ KDBUS_ITEM_TIMESTAMP,
+ KDBUS_ITEM_CREDS,
+ KDBUS_ITEM_PID_COMM,
+ KDBUS_ITEM_TID_COMM,
+ KDBUS_ITEM_EXE,
+ KDBUS_ITEM_CMDLINE,
+ KDBUS_ITEM_CGROUP,
+ KDBUS_ITEM_CAPS,
+ KDBUS_ITEM_SECLABEL,
+ KDBUS_ITEM_AUDIT,
+ KDBUS_ITEM_CONN_NAME,
+
+ _KDBUS_ITEM_KERNEL_BASE = 0x3000,
+ KDBUS_ITEM_NAME_ADD = _KDBUS_ITEM_KERNEL_BASE,
+ KDBUS_ITEM_NAME_REMOVE,
+ KDBUS_ITEM_NAME_CHANGE,
+ KDBUS_ITEM_ID_ADD,
+ KDBUS_ITEM_ID_REMOVE,
+ KDBUS_ITEM_REPLY_TIMEOUT,
+ KDBUS_ITEM_REPLY_DEAD,
+};
+
+/**
+ * struct kdbus_item - chain of data blocks
+ * @size: Overall data record size
+ * @type: Kdbus_item type of data
+ * @data: Generic bytes
+ * @data32: Generic 32 bit array
+ * @data64: Generic 64 bit array
+ * @str: Generic string
+ * @id: Connection ID
+ * @vec: KDBUS_ITEM_PAYLOAD_VEC
+ * @creds: KDBUS_ITEM_CREDS
+ * @audit: KDBUS_ITEM_AUDIT
+ * @timestamp: KDBUS_ITEM_TIMESTAMP
+ * @name: KDBUS_ITEM_NAME
+ * @memfd: KDBUS_ITEM_PAYLOAD_MEMFD
+ * @name_change: KDBUS_ITEM_NAME_ADD
+ * KDBUS_ITEM_NAME_REMOVE
+ * KDBUS_ITEM_NAME_CHANGE
+ * @id_change: KDBUS_ITEM_ID_ADD
+ * KDBUS_ITEM_ID_REMOVE
+ * @policy: KDBUS_ITEM_POLICY_NAME
+ * KDBUS_ITEM_POLICY_ACCESS
+ */
+struct kdbus_item {
+ __u64 size;
+ __u64 type;
+ union {
+ __u8 data[0];
+ __u32 data32[0];
+ __u64 data64[0];
+ char str[0];
+
+ __u64 id;
+ struct kdbus_vec vec;
+ struct kdbus_creds creds;
+ struct kdbus_audit audit;
+ struct kdbus_timestamp timestamp;
+ struct kdbus_name name;
+ struct kdbus_memfd memfd;
+ int fds[0];
+ struct kdbus_notify_name_change name_change;
+ struct kdbus_notify_id_change id_change;
+ struct kdbus_policy policy;
+ };
+};
+
+/**
+ * enum kdbus_msg_flags - type of message
+ * @KDBUS_MSG_FLAGS_EXPECT_REPLY: Expect a reply message, used for
+ * method calls. The userspace-supplied
+ * cookie identifies the message and the
+ * respective reply carries the cookie
+ * in cookie_reply
+ * @KDBUS_MSG_FLAGS_SYNC_REPLY: Wait for destination connection to
+ * reply to this message. The
+ * KDBUS_CMD_MSG_SEND ioctl() will block
+ * until the reply is received, and
+ * offset_reply in struct kdbus_msg will
+ * yield the offset in the sender's pool
+ * where the reply can be found.
+ * This flag is only valid if
+ * @KDBUS_MSG_FLAGS_EXPECT_REPLY is set as
+ * well.
+ * @KDBUS_MSG_FLAGS_NO_AUTO_START: Do not start a service, if the addressed
+ * name is not currently active
+ */
+enum kdbus_msg_flags {
+ KDBUS_MSG_FLAGS_EXPECT_REPLY = 1 << 0,
+ KDBUS_MSG_FLAGS_SYNC_REPLY = 1 << 1,
+ KDBUS_MSG_FLAGS_NO_AUTO_START = 1 << 2,
+};
+
+/**
+ * enum kdbus_payload_type - type of payload carried by message
+ * @KDBUS_PAYLOAD_KERNEL: Kernel-generated simple message
+ * @KDBUS_PAYLOAD_DBUS: D-Bus marshalling "DBusDBus"
+ */
+enum kdbus_payload_type {
+ KDBUS_PAYLOAD_KERNEL,
+ KDBUS_PAYLOAD_DBUS = 0x4442757344427573ULL,
+};
+
+/**
+ * struct kdbus_msg - the representation of a kdbus message
+ * @size: Total size of the message
+ * @flags: Message flags (KDBUS_MSG_FLAGS_*)
+ * @priority: Message queue priority value
+ * @dst_id: 64-bit ID of the destination connection
+ * @src_id: 64-bit ID of the source connection
+ * @payload_type: Payload type (KDBUS_PAYLOAD_*)
+ * @cookie: Userspace-supplied cookie, for the connection
+ * to identify its messages
+ * @timeout_ns: The time to wait for a message reply from the peer.
+ * If there is no reply, a kernel-generated message
+ * with an attached KDBUS_ITEM_REPLY_TIMEOUT item
+ * is sent to @src_id.
+ * @cookie_reply: A reply to the requesting message with the same
+ * cookie. The requesting connection can match its
+ * request and the reply with this value
+ * @offset_reply: If KDBUS_MSG_FLAGS_WAIT_FOR_REPLY, this field will
+ * contain the offset in the sender's pool where the
+ * reply is stored.
+ * @items: A list of kdbus_items containing the message payload
+ */
+struct kdbus_msg {
+ __u64 size;
+ __u64 flags;
+ __s64 priority;
+ __u64 dst_id;
+ __u64 src_id;
+ __u64 payload_type;
+ __u64 cookie;
+ union {
+ __u64 timeout_ns;
+ __u64 cookie_reply;
+ __u64 offset_reply;
+ };
+ struct kdbus_item items[0];
+} __attribute__((aligned(8)));
+
+/**
+ * enum kdbus_recv_flags - flags for de-queuing messages
+ * @KDBUS_RECV_PEEK: Return the next queued message without
+ * actually de-queuing it, and without installing
+ * any file descriptors or other resources. It is
+ * usually used to determine the activating
+ * connection of a bus name.
+ * @KDBUS_RECV_DROP: Drop and free the next queued message and all
+ * its resources without actually receiving it.
+ * @KDBUS_RECV_USE_PRIORITY: Only de-queue messages with the specified or
+ * higher priority (lowest values); if not set,
+ * the priority value is ignored.
+ */
+enum kdbus_recv_flags {
+ KDBUS_RECV_PEEK = 1 << 0,
+ KDBUS_RECV_DROP = 1 << 1,
+ KDBUS_RECV_USE_PRIORITY = 1 << 2,
+};
+
+/**
+ * struct kdbus_cmd_recv - struct to de-queue a buffered message
+ * @flags: KDBUS_RECV_* flags
+ * @priority: Minimum priority of the messages to de-queue. Lowest
+ * values have the highest priority.
+ * @offset: Returned offset in the pool where the message is
+ * stored. The user must use KDBUS_CMD_FREE to free
+ * the allocated memory.
+ *
+ * This struct is used with the KDBUS_CMD_MSG_RECV ioctl.
+ */
+struct kdbus_cmd_recv {
+ __u64 flags;
+ __s64 priority;
+ __u64 offset;
+} __attribute__((aligned(8)));
+
+/**
+ * enum kdbus_policy_access_type - permissions of a policy record
+ * @_KDBUS_POLICY_ACCESS_NULL: Uninitialized/invalid
+ * @KDBUS_POLICY_ACCESS_USER: Grant access to a uid
+ * @KDBUS_POLICY_ACCESS_GROUP: Grant access to gid
+ * @KDBUS_POLICY_ACCESS_WORLD: World-accessible
+ */
+enum kdbus_policy_access_type {
+ _KDBUS_POLICY_ACCESS_NULL,
+ KDBUS_POLICY_ACCESS_USER,
+ KDBUS_POLICY_ACCESS_GROUP,
+ KDBUS_POLICY_ACCESS_WORLD,
+};
+
+/**
+ * enum kdbus_policy_access_flags - mode flags
+ * @KDBUS_POLICY_RECV: Allow receive
+ * @KDBUS_POLICY_SEND: Allow send
+ * @KDBUS_POLICY_OWN: Allow to own a well-known name
+ */
+enum kdbus_policy_type {
+ KDBUS_POLICY_RECV = 1 << 2,
+ KDBUS_POLICY_SEND = 1 << 1,
+ KDBUS_POLICY_OWN = 1 << 0,
+};
+
+/**
+ * struct kdbus_cmd_policy - a series of policies to upload
+ * @size: The total size of the structure
+ * @policies: The policies to upload
+ *
+ * A KDBUS_POLICY_NAME must always preceeds a KDBUS_POLICY_ACCESS entry.
+ * A new KDBUS_POLICY_NAME can be added after KDBUS_POLICY_ACCESS for
+ * chaining multiple policies together.
+ */
+struct kdbus_cmd_policy {
+ __u64 size;
+ struct kdbus_item policies[0];
+} __attribute__((aligned(8)));
+
+/**
+ * enum kdbus_hello_flags - flags for struct kdbus_cmd_hello
+ * @KDBUS_HELLO_ACCEPT_FD: The connection allows the receiving of
+ * any passed file descriptors
+ * @KDBUS_HELLO_ACTIVATOR: Special-purpose connection which registers
+ * a well-know name for a process to be started
+ * when traffic arrives
+ * @KDBUS_HELLO_MONITOR: Special-purpose connection to monitor
+ * bus traffic
+ */
+enum kdbus_hello_flags {
+ KDBUS_HELLO_ACCEPT_FD = 1 << 0,
+ KDBUS_HELLO_ACTIVATOR = 1 << 1,
+ KDBUS_HELLO_MONITOR = 1 << 2,
+};
+
+/**
+ * enum kdbus_attach_flags - flags for metadata attachments
+ * @KDBUS_ATTACH_TIMESTAMP: Timestamp
+ * @KDBUS_ATTACH_CREDS: Credentials
+ * @KDBUS_ATTACH_NAMES: Well-known names
+ * @KDBUS_ATTACH_COMM: The "comm" process identifier
+ * @KDBUS_ATTACH_EXE: The path of the executable
+ * @KDBUS_ATTACH_CMDLINE: The process command line
+ * @KDBUS_ATTACH_CGROUP: The croup membership
+ * @KDBUS_ATTACH_CAPS: The process capabilities
+ * @KDBUS_ATTACH_SECLABEL: The security label
+ * @KDBUS_ATTACH_AUDIT: The audit IDs
+ * @KDBUS_ATTACH_CONN_NAME: The human-readable connection name
+ */
+enum kdbus_attach_flags {
+ KDBUS_ATTACH_TIMESTAMP = 1 << 0,
+ KDBUS_ATTACH_CREDS = 1 << 1,
+ KDBUS_ATTACH_NAMES = 1 << 2,
+ KDBUS_ATTACH_COMM = 1 << 3,
+ KDBUS_ATTACH_EXE = 1 << 4,
+ KDBUS_ATTACH_CMDLINE = 1 << 5,
+ KDBUS_ATTACH_CGROUP = 1 << 6,
+ KDBUS_ATTACH_CAPS = 1 << 7,
+ KDBUS_ATTACH_SECLABEL = 1 << 8,
+ KDBUS_ATTACH_AUDIT = 1 << 9,
+ KDBUS_ATTACH_CONN_NAME = 1 << 10,
+};
+
+/**
+ * struct kdbus_cmd_hello - struct to say hello to kdbus
+ * @size: The total size of the structure
+ * @conn_flags: Connection flags (KDBUS_HELLO_*). The kernel will
+ * return its capabilities in that field.
+ * @attach_flags: Mask of metadata to attach to each message sent
+ * (KDBUS_ATTACH_*)
+ * @bus_flags: The flags field copied verbatim from the original
+ * KDBUS_CMD_BUS_MAKE ioctl. It's intended to be useful
+ * to do negotiation of features of the payload that is
+ * transferred (kernel â userspace)
+ * @id: The ID of this connection (kernel â userspace)
+ * @bloom_size: The bloom filter size chosen by the owner
+ * (kernel â userspace)
+ * @pool_size: Size of the connection's buffer where the received
+ * messages are placed
+ * @id128: Unique 128-bit ID of the bus (kernel â userspace)
+ * @items: A list of items
+ *
+ * This struct is used with the KDBUS_CMD_HELLO ioctl.
+ */
+struct kdbus_cmd_hello {
+ __u64 size;
+ __u64 conn_flags;
+ __u64 attach_flags;
+ __u64 bus_flags;
+ __u64 id;
+ __u64 bloom_size;
+ __u64 pool_size;
+ __u8 id128[16];
+ struct kdbus_item items[0];
+} __attribute__((aligned(8)));
+
+/* Flags for KDBUS_CMD_{BUS,EP,NS}_MAKE */
+enum kdbus_make_flags {
+ KDBUS_MAKE_ACCESS_GROUP = 1 << 0,
+ KDBUS_MAKE_ACCESS_WORLD = 1 << 1,
+ KDBUS_MAKE_POLICY_OPEN = 1 << 2,
+};
+
+/**
+ * struct kdbus_cmd_make - struct to make a bus, an endpoint or a namespace
+ * @size: The total size of the struct
+ * @flags: Properties for the bus/ep/ns to create
+ * @items: Items describing details
+ *
+ * This structure is used with the KDBUS_CMD_BUS_MAKE, KDBUS_CMD_EP_MAKE and
+ * KDBUS_CMD_NS_MAKE ioctls.
+ */
+struct kdbus_cmd_make {
+ __u64 size;
+ __u64 flags;
+ struct kdbus_item items[0];
+} __attribute__((aligned(8)));
+
+/**
+ * enum kdbus_name_flags - properties of a well-known name
+ * @KDBUS_NAME_REPLACE_EXISTING: Try to replace name of other connections
+ * @KDBUS_NAME_ALLOW_REPLACEMENT: Allow the replacement of the name
+ * @KDBUS_NAME_QUEUE: Name should be queued if busy
+ * @KDBUS_NAME_IN_QUEUE: Name is queued
+ * @KDBUS_NAME_ACTIVATOR: Name is owned by a activator connection
+ */
+enum kdbus_name_flags {
+ KDBUS_NAME_REPLACE_EXISTING = 1 << 0,
+ KDBUS_NAME_ALLOW_REPLACEMENT = 1 << 1,
+ KDBUS_NAME_QUEUE = 1 << 2,
+ KDBUS_NAME_IN_QUEUE = 1 << 3,
+ KDBUS_NAME_ACTIVATOR = 1 << 4,
+};
+
+/**
+ * struct kdbus_cmd_name - struct to describe a well-known name
+ * @size: The total size of the struct
+ * @flags: Flags for a name entry (KDBUS_NAME_*)
+ * @owner_id: The current owner of the name. For requests,
+ * privileged users may set this field to
+ * (de)register names on behalf of other connections.
+ * @conn_flags: The flags of the owning connection (KDBUS_HELLO_*)
+ * @name: The well-known name
+ *
+ * This structure is used with the KDBUS_CMD_NAME_ACQUIRE ioctl.
+ */
+struct kdbus_cmd_name {
+ __u64 size;
+ __u64 flags;
+ __u64 owner_id;
+ __u64 conn_flags;
+ char name[0];
+} __attribute__((aligned(8)));
+
+/**
+ * enum kdbus_name_list_flags - what to include into the returned list
+ * @KDBUS_NAME_LIST_UNIQUE: All active connections
+ * @KDBUS_NAME_LIST_NAMES: All known well-known names
+ * @KDBUS_NAME_LIST_ACTIVATORS: All activator connections
+ * @KDBUS_NAME_LIST_QUEUED: All queued-up names
+ */
+enum kdbus_name_list_flags {
+ KDBUS_NAME_LIST_UNIQUE = 1 << 0,
+ KDBUS_NAME_LIST_NAMES = 1 << 1,
+ KDBUS_NAME_LIST_ACTIVATORS = 1 << 2,
+ KDBUS_NAME_LIST_QUEUED = 1 << 3,
+};
+
+/**
+ * struct kdbus_cmd_name_list - request a list of name entries
+ * @flags: Flags for the query (KDBUS_NAME_LIST_*)
+ * @offset: The returned offset in the caller's pool buffer.
+ * The user must use KDBUS_CMD_FREE to free the
+ * allocated memory.
+ *
+ * This structure is used with the KDBUS_CMD_NAME_LIST ioctl.
+ */
+struct kdbus_cmd_name_list {
+ __u64 flags;
+ __u64 offset;
+} __attribute__((aligned(8)));
+
+/**
+ * struct kdbus_name_list - information returned by KDBUS_CMD_NAME_LIST
+ * @size: The total size of the structure
+ * @names: A list of names
+ *
+ * Note that the user is responsible for freeing the allocated memory with
+ * the KDBUS_CMD_FREE ioctl.
+ */
+struct kdbus_name_list {
+ __u64 size;
+ struct kdbus_cmd_name names[0];
+};
+
+/**
+ * struct kdbus_cmd_conn_info - struct used for KDBUS_CMD_CONN_INFO ioctl
+ * @size: The total size of the struct
+ * @flags: KDBUS_ATTACH_* flags
+ * @id: The 64-bit ID of the connection. If set to zero, passing
+ * @name is required. kdbus will look up the name to
+ * determine the ID in this case.
+ * @offset: Returned offset in the caller's pool buffer where the
+ * kdbus_conn_info struct result is stored. The user must
+ * use KDBUS_CMD_FREE to free the allocated memory.
+ * @name: The optional well-known name to look up. Only needed in
+ * case @id is zero.
+ *
+ * On success, the KDBUS_CMD_CONN_INFO ioctl will return 0 and @offset will
+ * tell the user the offset in the connection pool buffer at which to find the
+ * result in a struct kdbus_conn_info.
+ */
+struct kdbus_cmd_conn_info {
+ __u64 size;
+ __u64 flags;
+ __u64 id;
+ __u64 offset;
+ char name[0];
+} __attribute__((aligned(8)));
+
+/**
+ * struct kdbus_conn_info - information returned by KDBUS_CMD_CONN_INFO
+ * @size: The total size of the struct
+ * @id: The connection's 64-bit ID
+ * @flags: The connection's flags
+ * @items: A list of struct kdbus_item
+ *
+ * Note that the user is responsible for freeing the allocated memory with
+ * the KDBUS_CMD_FREE ioctl.
+ */
+struct kdbus_conn_info {
+ __u64 size;
+ __u64 id;
+ __u64 flags;
+ struct kdbus_item items[0];
+};
+
+/**
+ * struct kdbus_cmd_match - struct to add or remove matches
+ * @size: The total size of the struct
+ * @owner_id: Privileged users may (de)register matches on behalf
+ * of other peers
+ * @cookie: Userspace supplied cookie. When removing, the cookie
+ * identifies the match to remove
+ * @items: A list of items for additional information
+ *
+ * This structure is used with the KDBUS_CMD_ADD_MATCH and
+ * KDBUS_CMD_REMOVE_MATCH ioctl.
+ */
+struct kdbus_cmd_match {
+ __u64 size;
+ __u64 owner_id;
+ __u64 cookie;
+ struct kdbus_item items[0];
+} __attribute__((aligned(8)));
+
+/**
+ * struct kdbus_cmd_memfd_make - create a kdbus memfd
+ * @size: The total size of the struct
+ * @file_size: The initial file size
+ * @fd: The returned file descriptor number
+ * @__pad: Padding to ensure proper alignement
+ * @items: A list of items for additional information
+ *
+ * This structure is used with the KDBUS_CMD_MEMFD_NEW ioctl.
+ */
+struct kdbus_cmd_memfd_make {
+ __u64 size;
+ __u64 file_size;
+ int fd;
+ __u32 __pad;
+ struct kdbus_item items[0];
+} __attribute__((aligned(8)));
+
+/**
+ * enum kdbus_ioctl_type - Ioctl API
+ * @KDBUS_CMD_BUS_MAKE: After opening the "control" device node, this
+ * command creates a new bus with the specified
+ * name. The bus is immediately shut down and
+ * cleaned up when the opened "control" device node
+ * is closed.
+ * @KDBUS_CMD_NS_MAKE: Similar to KDBUS_CMD_BUS_MAKE, but it creates a
+ * new kdbus namespace.
+ * @KDBUS_CMD_EP_MAKE: Creates a new named special endpoint to talk to
+ * the bus. Such endpoints usually carry a more
+ * restrictive policy and grant restricted access
+ * to specific applications.
+ * @KDBUS_CMD_HELLO: By opening the bus device node a connection is
+ * created. After a HELLO the opened connection
+ * becomes an active peer on the bus.
+ * @KDBUS_CMD_BYEBYE: Disconnect a connection. If the connection's
+ * message list is empty, the calls succeeds, and
+ * the handle is rendered unusable. Otherwise,
+ * -EAGAIN is returned without any further side-
+ * effects.
+ * @KDBUS_CMD_MSG_SEND: Send a message and pass data from userspace to
+ * the kernel.
+ * @KDBUS_CMD_MSG_RECV: Receive a message from the kernel which is
+ * placed in the receiver's pool.
+ * @KDBUS_CMD_FREE: Release the allocated memory in the receiver's
+ * pool.
+ * @KDBUS_CMD_NAME_ACQUIRE: Request a well-known bus name to associate with
+ * the connection. Well-known names are used to
+ * address a peer on the bus.
+ * @KDBUS_CMD_NAME_RELEASE: Release a well-known name the connection
+ * currently owns.
+ * @KDBUS_CMD_NAME_LIST: Retrieve the list of all currently registered
+ * well-known and unique names.
+ * @KDBUS_CMD_CONN_INFO: Retrieve credentials and properties of the
+ * initial creator of the connection. The data was
+ * stored at registration time and does not
+ * necessarily represent the connected process or
+ * the actual state of the process.
+ * @KDBUS_CMD_MATCH_ADD: Install a match which broadcast messages should
+ * be delivered to the connection.
+ * @KDBUS_CMD_MATCH_REMOVE: Remove a current match for broadcast messages.
+ * @KDBUS_CMD_EP_POLICY_SET: Set the policy of an endpoint. It is used to
+ * restrict the access for endpoints created with
+ * KDBUS_CMD_EP_MAKE.
+ * @KDBUS_CMD_MEMFD_NEW: Return a new file descriptor which provides an
+ * anonymous shared memory file and which can be
+ * used to pass around larger chunks of data.
+ * Kdbus memfd files can be sealed, which allows
+ * the receiver to trust the data it has received.
+ * Kdbus memfd files expose only very limited
+ * operations, they can be mmap()ed, seek()ed,
+ * (p)read(v)() and (p)write(v)(); most other
+ * common file operations are not implemented.
+ * Special caution needs to be taken with
+ * read(v)()/write(v)() on a shared file; the
+ * underlying file position is always shared
+ * between all users of the file and race against
+ * each other, pread(v)()/pwrite(v)() avoid these
+ * issues.
+ * @KDBUS_CMD_MEMFD_SIZE_GET: Return the size of the underlying file, which
+ * changes with write().
+ * @KDBUS_CMD_MEMFD_SIZE_SET: Truncate the underlying file to the specified
+ * size.
+ * @KDBUS_CMD_MEMFD_SEAL_GET: Return the state of the file sealing.
+ * @KDBUS_CMD_MEMFD_SEAL_SET: Seal or break a seal of the file. Only files
+ * which are not shared with other processes and
+ * which are currently not mapped can be sealed.
+ * The current process needs to be the one and
+ * single owner of the file, the sealing cannot
+ * be changed as long as the file is shared.
+ */
+enum kdbus_ioctl_type {
+ KDBUS_CMD_BUS_MAKE = _IOW (KDBUS_IOC_MAGIC, 0x00, struct kdbus_cmd_make),
+ KDBUS_CMD_NS_MAKE = _IOR (KDBUS_IOC_MAGIC, 0x10, struct kdbus_cmd_make),
+ KDBUS_CMD_EP_MAKE = _IOW (KDBUS_IOC_MAGIC, 0x20, struct kdbus_cmd_make),
+
+ KDBUS_CMD_HELLO = _IOWR(KDBUS_IOC_MAGIC, 0x30, struct kdbus_cmd_hello),
+ KDBUS_CMD_BYEBYE = _IO (KDBUS_IOC_MAGIC, 0x31),
+
+ KDBUS_CMD_MSG_SEND = _IOW (KDBUS_IOC_MAGIC, 0x40, struct kdbus_msg),
+ KDBUS_CMD_MSG_RECV = _IOWR(KDBUS_IOC_MAGIC, 0x41, struct kdbus_cmd_recv),
+ KDBUS_CMD_FREE = _IOW (KDBUS_IOC_MAGIC, 0x42, __u64 *),
+
+ KDBUS_CMD_NAME_ACQUIRE = _IOWR(KDBUS_IOC_MAGIC, 0x50, struct kdbus_cmd_name),
+ KDBUS_CMD_NAME_RELEASE = _IOW (KDBUS_IOC_MAGIC, 0x51, struct kdbus_cmd_name),
+ KDBUS_CMD_NAME_LIST = _IOWR(KDBUS_IOC_MAGIC, 0x52, struct kdbus_cmd_name_list),
+
+ KDBUS_CMD_CONN_INFO = _IOWR(KDBUS_IOC_MAGIC, 0x60, struct kdbus_cmd_conn_info),
+
+ KDBUS_CMD_MATCH_ADD = _IOW (KDBUS_IOC_MAGIC, 0x70, struct kdbus_cmd_match),
+ KDBUS_CMD_MATCH_REMOVE = _IOW (KDBUS_IOC_MAGIC, 0x71, struct kdbus_cmd_match),
+
+ KDBUS_CMD_EP_POLICY_SET = _IOW (KDBUS_IOC_MAGIC, 0x80, struct kdbus_cmd_policy),
+
+ KDBUS_CMD_MEMFD_NEW = _IOWR(KDBUS_IOC_MAGIC, 0xc0, struct kdbus_cmd_memfd_make),
+ KDBUS_CMD_MEMFD_SIZE_GET = _IOR (KDBUS_IOC_MAGIC, 0xc1, __u64 *),
+ KDBUS_CMD_MEMFD_SIZE_SET = _IOW (KDBUS_IOC_MAGIC, 0xc2, __u64 *),
+ KDBUS_CMD_MEMFD_SEAL_GET = _IOR (KDBUS_IOC_MAGIC, 0xc3, int *),
+ KDBUS_CMD_MEMFD_SEAL_SET = _IO (KDBUS_IOC_MAGIC, 0xc4),
+};
+
+/*
+ * errno - api error codes
+ * @E2BIG: A message contains too many records or items.
+ * @EADDRINUSE: A well-known bus name is already taken by another
+ * connection.
+ * @EADDRNOTAVAIL: A message flagged not to activate a service, addressed
+ * a service which is not currently running.
+ * @EAGAIN: No messages are queued at the moment.
+ * @EBADF: File descriptors passed with the message are not valid.
+ * @EBADFD: A bus connection is in a corrupted state.
+ * @EBADMSG: Passed data contains a combination of conflicting or
+ * inconsistent types.
+ * @EBUSY: The user tried to say BYEBYE to a connection, but the
+ * connection had a non-empty message list.
+ * @ECONNRESET: A connection is shut down, no further operations are
+ * possible.
+ * @ECOMM: A peer does not accept the file descriptors addressed
+ * to it.
+ * @EDESTADDRREQ: The well-known bus name is required but missing.
+ * @EDOM: The size of data does not match the expectations. Used
+ * for the size of the bloom filter bit field.
+ * @EEXIST: A requested namespace, bus or endpoint with the same
+ * name already exists. A specific data type, which is
+ * only expected once, is provided multiple times.
+ * @EFAULT: The supplied memory could not be accessed, or the data
+ * is not properly aligned.
+ * @EINVAL: The provided data does not match its type or other
+ * expectations, like a string which is not NUL terminated,
+ * or a string length that points behind the first
+ * \0-byte in the string.
+ * @EMEDIUMTYPE: A file descriptor which is not a kdbus memfd was
+ * refused to send as KDBUS_MSG_PAYLOAD_MEMFD.
+ * @EMFILE: Too many file descriptors have been supplied with a
+ * message.
+ * @EMLINK: Too many requests from this connection to other peers
+ * are queued and waiting for a reply
+ * @EMSGSIZE: The supplied data is larger than the allowed maximum
+ * size.
+ * @ENAMETOOLONG: The requested name is larger than the allowed maximum
+ * size.
+ * @ENOBUFS: There is no space left for the submitted data to fit
+ * into the receiver's pool.
+ * @ENOENT: The name to query information about is currently not on
+ * the bus.
+ * @ENOMEM: Out of memory.
+ * @ENOMSG: The queue is not empty, but no message with a matching
+ * priority is currently queued.
+ * @ENOSYS: The requested functionality is not available.
+ * @ENOTSUPP: The feature negotiation failed, a not supported feature
+ * was requested, or an unknown item type was received.
+ * @ENOTTY: An unknown ioctl command was received.
+ * @ENOTUNIQ: A specific data type was addressed to a broadcast
+ * address, but only direct addresses support this kind of
+ * data.
+ * @ENXIO: A unique address does not exist, or an offset in the
+ * receiver's pool does not represent a queued message.
+ * @EPERM: The policy prevented an operation. The requested
+ * resource is owned by another entity.
+ * @ESHUTDOWN: A namespace or endpoint is currently shutting down;
+ * no further operations will be possible.
+ * @ESRCH: A requested well-known bus name is not found.
+ * @ETXTBSY: A kdbus memfd file cannot be sealed or the seal removed,
+ * because it is shared with other processes or still
+ * mmap()ed.
+ * @EXFULL: The size limits in the pool are reached, no data of
+ * the size tried to submit can be queued.
+ */
+#endif
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
new file mode 100644
index 0000000..95d80db
--- /dev/null
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -0,0 +1,3032 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <endian.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/poll.h>
+#include <byteswap.h>
+#include <sys/mman.h>
+#include <pthread.h>
+
+#include "util.h"
+#include "macro.h"
+#include "strv.h"
+#include "set.h"
+#include "missing.h"
+#include "def.h"
+
+#include "sd-bus.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-type.h"
+#include "bus-socket.h"
+#include "bus-kernel.h"
+#include "bus-control.h"
+#include "bus-introspect.h"
+#include "bus-signature.h"
+#include "bus-objects.h"
+#include "bus-util.h"
+#include "bus-container.h"
+#include "bus-protocol.h"
+
+static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec);
+static int attach_io_events(sd_bus *b);
+static void detach_io_events(sd_bus *b);
+
+static void bus_close_fds(sd_bus *b) {
+ assert(b);
+
+ detach_io_events(b);
+
+ if (b->input_fd >= 0)
+ close_nointr_nofail(b->input_fd);
+
+ if (b->output_fd >= 0 && b->output_fd != b->input_fd)
+ close_nointr_nofail(b->output_fd);
+
+ b->input_fd = b->output_fd = -1;
+}
+
+static void bus_node_destroy(sd_bus *b, struct node *n) {
+ struct node_callback *c;
+ struct node_vtable *v;
+ struct node_enumerator *e;
+
+ assert(b);
+
+ if (!n)
+ return;
+
+ while (n->child)
+ bus_node_destroy(b, n->child);
+
+ while ((c = n->callbacks)) {
+ LIST_REMOVE(callbacks, n->callbacks, c);
+ free(c);
+ }
+
+ while ((v = n->vtables)) {
+ LIST_REMOVE(vtables, n->vtables, v);
+ free(v->interface);
+ free(v);
+ }
+
+ while ((e = n->enumerators)) {
+ LIST_REMOVE(enumerators, n->enumerators, e);
+ free(e);
+ }
+
+ if (n->parent)
+ LIST_REMOVE(siblings, n->parent->child, n);
+
+ assert_se(hashmap_remove(b->nodes, n->path) == n);
+ free(n->path);
+ free(n);
+}
+
+static void bus_reset_queues(sd_bus *b) {
+ unsigned i;
+
+ assert(b);
+
+ for (i = 0; i < b->rqueue_size; i++)
+ sd_bus_message_unref(b->rqueue[i]);
+ free(b->rqueue);
+
+ for (i = 0; i < b->wqueue_size; i++)
+ sd_bus_message_unref(b->wqueue[i]);
+ free(b->wqueue);
+
+ b->rqueue = b->wqueue = NULL;
+ b->rqueue_allocated = b->wqueue_allocated = 0;
+ b->rqueue_size = b->wqueue_size = 0;
+}
+
+static void bus_free(sd_bus *b) {
+ struct filter_callback *f;
+ struct node *n;
+
+ assert(b);
+
+ sd_bus_detach_event(b);
+
+ bus_close_fds(b);
+
+ if (b->kdbus_buffer)
+ munmap(b->kdbus_buffer, KDBUS_POOL_SIZE);
+
+ free(b->rbuffer);
+ free(b->unique_name);
+ free(b->auth_buffer);
+ free(b->address);
+ free(b->kernel);
+ free(b->machine);
+ free(b->fake_label);
+ free(b->cgroup_root);
+
+ free(b->exec_path);
+ strv_free(b->exec_argv);
+
+ close_many(b->fds, b->n_fds);
+ free(b->fds);
+
+ bus_reset_queues(b);
+
+ hashmap_free_free(b->reply_callbacks);
+ prioq_free(b->reply_callbacks_prioq);
+
+ while ((f = b->filter_callbacks)) {
+ LIST_REMOVE(callbacks, b->filter_callbacks, f);
+ free(f);
+ }
+
+ bus_match_free(&b->match_callbacks);
+
+ hashmap_free_free(b->vtable_methods);
+ hashmap_free_free(b->vtable_properties);
+
+ while ((n = hashmap_first(b->nodes)))
+ bus_node_destroy(b, n);
+
+ hashmap_free(b->nodes);
+
+ bus_kernel_flush_memfd(b);
+
+ assert_se(pthread_mutex_destroy(&b->memfd_cache_mutex) == 0);
+
+ free(b);
+}
+
+_public_ int sd_bus_new(sd_bus **ret) {
+ sd_bus *r;
+
+ assert_return(ret, -EINVAL);
+
+ r = new0(sd_bus, 1);
+ if (!r)
+ return -ENOMEM;
+
+ r->n_ref = REFCNT_INIT;
+ r->input_fd = r->output_fd = -1;
+ r->message_version = 1;
+ r->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME;
+ r->hello_flags |= KDBUS_HELLO_ACCEPT_FD;
+ r->attach_flags |= KDBUS_ATTACH_NAMES;
+ r->original_pid = getpid();
+
+ assert_se(pthread_mutex_init(&r->memfd_cache_mutex, NULL) == 0);
+
+ /* We guarantee that wqueue always has space for at least one
+ * entry */
+ if (!GREEDY_REALLOC(r->wqueue, r->wqueue_allocated, 1)) {
+ free(r);
+ return -ENOMEM;
+ }
+
+ *ret = r;
+ return 0;
+}
+
+_public_ int sd_bus_set_address(sd_bus *bus, const char *address) {
+ char *a;
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(address, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ a = strdup(address);
+ if (!a)
+ return -ENOMEM;
+
+ free(bus->address);
+ bus->address = a;
+
+ return 0;
+}
+
+_public_ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(input_fd >= 0, -EINVAL);
+ assert_return(output_fd >= 0, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ bus->input_fd = input_fd;
+ bus->output_fd = output_fd;
+ return 0;
+}
+
+_public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) {
+ char *p, **a;
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(path, -EINVAL);
+ assert_return(!strv_isempty(argv), -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ p = strdup(path);
+ if (!p)
+ return -ENOMEM;
+
+ a = strv_copy(argv);
+ if (!a) {
+ free(p);
+ return -ENOMEM;
+ }
+
+ free(bus->exec_path);
+ strv_free(bus->exec_argv);
+
+ bus->exec_path = p;
+ bus->exec_argv = a;
+
+ return 0;
+}
+
+_public_ int sd_bus_set_bus_client(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ bus->bus_client = !!b;
+ return 0;
+}
+
+_public_ int sd_bus_negotiate_fds(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ SET_FLAG(bus->hello_flags, KDBUS_HELLO_ACCEPT_FD, b);
+ return 0;
+}
+
+_public_ int sd_bus_negotiate_attach_timestamp(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ SET_FLAG(bus->attach_flags, KDBUS_ATTACH_TIMESTAMP, b);
+ return 0;
+}
+
+_public_ int sd_bus_negotiate_attach_creds(sd_bus *bus, uint64_t mask) {
+ assert_return(bus, -EINVAL);
+ assert_return(mask <= _SD_BUS_CREDS_ALL, -EINVAL);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ /* The well knowns we need unconditionally, so that matches can work */
+ bus->creds_mask = mask | SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME;
+
+ return kdbus_translate_attach_flags(bus->creds_mask, &bus->creds_mask);
+}
+
+_public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) {
+ assert_return(bus, -EINVAL);
+ assert_return(b || sd_id128_equal(server_id, SD_ID128_NULL), -EINVAL);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ bus->is_server = !!b;
+ bus->server_id = server_id;
+ return 0;
+}
+
+_public_ int sd_bus_set_anonymous(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ bus->anonymous_auth = !!b;
+ return 0;
+}
+
+_public_ int sd_bus_set_trusted(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ bus->trusted = !!b;
+ return 0;
+}
+
+static int hello_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+ const char *s;
+ int r;
+
+ assert(bus);
+ assert(bus->state == BUS_HELLO || bus->state == BUS_CLOSING);
+ assert(reply);
+
+ r = sd_bus_message_get_errno(reply);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return -r;
+
+ r = sd_bus_message_read(reply, "s", &s);
+ if (r < 0)
+ return r;
+
+ if (!service_name_is_valid(s) || s[0] != ':')
+ return -EBADMSG;
+
+ bus->unique_name = strdup(s);
+ if (!bus->unique_name)
+ return -ENOMEM;
+
+ if (bus->state == BUS_HELLO)
+ bus->state = BUS_RUNNING;
+
+ return 1;
+}
+
+static int bus_send_hello(sd_bus *bus) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ assert(bus);
+
+ if (!bus->bus_client || bus->is_kernel)
+ return 0;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "Hello",
+ &m);
+ if (r < 0)
+ return r;
+
+ return sd_bus_call_async(bus, m, hello_callback, NULL, 0, &bus->hello_cookie);
+}
+
+int bus_start_running(sd_bus *bus) {
+ assert(bus);
+
+ if (bus->bus_client && !bus->is_kernel) {
+ bus->state = BUS_HELLO;
+ return 1;
+ }
+
+ bus->state = BUS_RUNNING;
+ return 1;
+}
+
+static int parse_address_key(const char **p, const char *key, char **value) {
+ size_t l, n = 0, allocated = 0;
+ const char *a;
+ char *r = NULL;
+
+ assert(p);
+ assert(*p);
+ assert(value);
+
+ if (key) {
+ l = strlen(key);
+ if (strncmp(*p, key, l) != 0)
+ return 0;
+
+ if ((*p)[l] != '=')
+ return 0;
+
+ if (*value)
+ return -EINVAL;
+
+ a = *p + l + 1;
+ } else
+ a = *p;
+
+ while (*a != ';' && *a != ',' && *a != 0) {
+ char c;
+
+ if (*a == '%') {
+ int x, y;
+
+ x = unhexchar(a[1]);
+ if (x < 0) {
+ free(r);
+ return x;
+ }
+
+ y = unhexchar(a[2]);
+ if (y < 0) {
+ free(r);
+ return y;
+ }
+
+ c = (char) ((x << 4) | y);
+ a += 3;
+ } else {
+ c = *a;
+ a++;
+ }
+
+ if (!GREEDY_REALLOC(r, allocated, n + 2))
+ return -ENOMEM;
+
+ r[n++] = c;
+ }
+
+ if (!r) {
+ r = strdup("");
+ if (!r)
+ return -ENOMEM;
+ } else
+ r[n] = 0;
+
+ if (*a == ',')
+ a++;
+
+ *p = a;
+
+ free(*value);
+ *value = r;
+
+ return 1;
+}
+
+static void skip_address_key(const char **p) {
+ assert(p);
+ assert(*p);
+
+ *p += strcspn(*p, ",");
+
+ if (**p == ',')
+ (*p) ++;
+}
+
+static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
+ _cleanup_free_ char *path = NULL, *abstract = NULL;
+ size_t l;
+ int r;
+
+ assert(b);
+ assert(p);
+ assert(*p);
+ assert(guid);
+
+ while (**p != 0 && **p != ';') {
+ r = parse_address_key(p, "guid", guid);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "path", &path);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "abstract", &abstract);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ skip_address_key(p);
+ }
+
+ if (!path && !abstract)
+ return -EINVAL;
+
+ if (path && abstract)
+ return -EINVAL;
+
+ if (path) {
+ l = strlen(path);
+ if (l > sizeof(b->sockaddr.un.sun_path))
+ return -E2BIG;
+
+ b->sockaddr.un.sun_family = AF_UNIX;
+ strncpy(b->sockaddr.un.sun_path, path, sizeof(b->sockaddr.un.sun_path));
+ b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l;
+ } else if (abstract) {
+ l = strlen(abstract);
+ if (l > sizeof(b->sockaddr.un.sun_path) - 1)
+ return -E2BIG;
+
+ b->sockaddr.un.sun_family = AF_UNIX;
+ b->sockaddr.un.sun_path[0] = 0;
+ strncpy(b->sockaddr.un.sun_path+1, abstract, sizeof(b->sockaddr.un.sun_path)-1);
+ b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
+ }
+
+ return 0;
+}
+
+static int parse_tcp_address(sd_bus *b, const char **p, char **guid) {
+ _cleanup_free_ char *host = NULL, *port = NULL, *family = NULL;
+ int r;
+ struct addrinfo *result, hints = {
+ .ai_socktype = SOCK_STREAM,
+ .ai_flags = AI_ADDRCONFIG,
+ };
+
+ assert(b);
+ assert(p);
+ assert(*p);
+ assert(guid);
+
+ while (**p != 0 && **p != ';') {
+ r = parse_address_key(p, "guid", guid);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "host", &host);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "port", &port);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "family", &family);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ skip_address_key(p);
+ }
+
+ if (!host || !port)
+ return -EINVAL;
+
+ if (family) {
+ if (streq(family, "ipv4"))
+ hints.ai_family = AF_INET;
+ else if (streq(family, "ipv6"))
+ hints.ai_family = AF_INET6;
+ else
+ return -EINVAL;
+ }
+
+ r = getaddrinfo(host, port, &hints, &result);
+ if (r == EAI_SYSTEM)
+ return -errno;
+ else if (r != 0)
+ return -EADDRNOTAVAIL;
+
+ memcpy(&b->sockaddr, result->ai_addr, result->ai_addrlen);
+ b->sockaddr_size = result->ai_addrlen;
+
+ freeaddrinfo(result);
+
+ return 0;
+}
+
+static int parse_exec_address(sd_bus *b, const char **p, char **guid) {
+ char *path = NULL;
+ unsigned n_argv = 0, j;
+ char **argv = NULL;
+ size_t allocated = 0;
+ int r;
+
+ assert(b);
+ assert(p);
+ assert(*p);
+ assert(guid);
+
+ while (**p != 0 && **p != ';') {
+ r = parse_address_key(p, "guid", guid);
+ if (r < 0)
+ goto fail;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "path", &path);
+ if (r < 0)
+ goto fail;
+ else if (r > 0)
+ continue;
+
+ if (startswith(*p, "argv")) {
+ unsigned ul;
+
+ errno = 0;
+ ul = strtoul(*p + 4, (char**) p, 10);
+ if (errno > 0 || **p != '=' || ul > 256) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ (*p) ++;
+
+ if (ul >= n_argv) {
+ if (!GREEDY_REALLOC0(argv, allocated, ul + 2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ n_argv = ul + 1;
+ }
+
+ r = parse_address_key(p, NULL, argv + ul);
+ if (r < 0)
+ goto fail;
+
+ continue;
+ }
+
+ skip_address_key(p);
+ }
+
+ if (!path) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ /* Make sure there are no holes in the array, with the
+ * exception of argv[0] */
+ for (j = 1; j < n_argv; j++)
+ if (!argv[j]) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ if (argv && argv[0] == NULL) {
+ argv[0] = strdup(path);
+ if (!argv[0]) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ b->exec_path = path;
+ b->exec_argv = argv;
+ return 0;
+
+fail:
+ for (j = 0; j < n_argv; j++)
+ free(argv[j]);
+
+ free(argv);
+ free(path);
+ return r;
+}
+
+static int parse_kernel_address(sd_bus *b, const char **p, char **guid) {
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(b);
+ assert(p);
+ assert(*p);
+ assert(guid);
+
+ while (**p != 0 && **p != ';') {
+ r = parse_address_key(p, "guid", guid);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "path", &path);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ skip_address_key(p);
+ }
+
+ if (!path)
+ return -EINVAL;
+
+ free(b->kernel);
+ b->kernel = path;
+ path = NULL;
+
+ return 0;
+}
+
+static int parse_container_unix_address(sd_bus *b, const char **p, char **guid) {
+ _cleanup_free_ char *machine = NULL;
+ int r;
+
+ assert(b);
+ assert(p);
+ assert(*p);
+ assert(guid);
+
+ while (**p != 0 && **p != ';') {
+ r = parse_address_key(p, "guid", guid);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "machine", &machine);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ skip_address_key(p);
+ }
+
+ if (!machine)
+ return -EINVAL;
+
+ if (!filename_is_safe(machine))
+ return -EINVAL;
+
+ free(b->machine);
+ b->machine = machine;
+ machine = NULL;
+
+ b->sockaddr.un.sun_family = AF_UNIX;
+ strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path));
+ b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + sizeof("/var/run/dbus/system_bus_socket") - 1;
+
+ return 0;
+}
+
+static int parse_container_kernel_address(sd_bus *b, const char **p, char **guid) {
+ _cleanup_free_ char *machine = NULL;
+ int r;
+
+ assert(b);
+ assert(p);
+ assert(*p);
+ assert(guid);
+
+ while (**p != 0 && **p != ';') {
+ r = parse_address_key(p, "guid", guid);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "machine", &machine);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ skip_address_key(p);
+ }
+
+ if (!machine)
+ return -EINVAL;
+
+ if (!filename_is_safe(machine))
+ return -EINVAL;
+
+ free(b->machine);
+ b->machine = machine;
+ machine = NULL;
+
+ free(b->kernel);
+ b->kernel = strdup("/dev/kdbus/0-system/bus");
+ if (!b->kernel)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void bus_reset_parsed_address(sd_bus *b) {
+ assert(b);
+
+ zero(b->sockaddr);
+ b->sockaddr_size = 0;
+ strv_free(b->exec_argv);
+ free(b->exec_path);
+ b->exec_path = NULL;
+ b->exec_argv = NULL;
+ b->server_id = SD_ID128_NULL;
+ free(b->kernel);
+ b->kernel = NULL;
+ free(b->machine);
+ b->machine = NULL;
+}
+
+static int bus_parse_next_address(sd_bus *b) {
+ _cleanup_free_ char *guid = NULL;
+ const char *a;
+ int r;
+
+ assert(b);
+
+ if (!b->address)
+ return 0;
+ if (b->address[b->address_index] == 0)
+ return 0;
+
+ bus_reset_parsed_address(b);
+
+ a = b->address + b->address_index;
+
+ while (*a != 0) {
+
+ if (*a == ';') {
+ a++;
+ continue;
+ }
+
+ if (startswith(a, "unix:")) {
+ a += 5;
+
+ r = parse_unix_address(b, &a, &guid);
+ if (r < 0)
+ return r;
+ break;
+
+ } else if (startswith(a, "tcp:")) {
+
+ a += 4;
+ r = parse_tcp_address(b, &a, &guid);
+ if (r < 0)
+ return r;
+
+ break;
+
+ } else if (startswith(a, "unixexec:")) {
+
+ a += 9;
+ r = parse_exec_address(b, &a, &guid);
+ if (r < 0)
+ return r;
+
+ break;
+
+ } else if (startswith(a, "kernel:")) {
+
+ a += 7;
+ r = parse_kernel_address(b, &a, &guid);
+ if (r < 0)
+ return r;
+
+ break;
+ } else if (startswith(a, "x-container-unix:")) {
+
+ a += 17;
+ r = parse_container_unix_address(b, &a, &guid);
+ if (r < 0)
+ return r;
+
+ break;
+ } else if (startswith(a, "x-container-kernel:")) {
+
+ a += 19;
+ r = parse_container_kernel_address(b, &a, &guid);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ a = strchr(a, ';');
+ if (!a)
+ return 0;
+ }
+
+ if (guid) {
+ r = sd_id128_from_string(guid, &b->server_id);
+ if (r < 0)
+ return r;
+ }
+
+ b->address_index = a - b->address;
+ return 1;
+}
+
+static int bus_start_address(sd_bus *b) {
+ int r;
+
+ assert(b);
+
+ for (;;) {
+ bool skipped = false;
+
+ bus_close_fds(b);
+
+ if (b->exec_path)
+ r = bus_socket_exec(b);
+ else if (b->machine && b->kernel)
+ r = bus_container_connect_kernel(b);
+ else if (b->machine && b->sockaddr.sa.sa_family != AF_UNSPEC)
+ r = bus_container_connect_socket(b);
+ else if (b->kernel)
+ r = bus_kernel_connect(b);
+ else if (b->sockaddr.sa.sa_family != AF_UNSPEC)
+ r = bus_socket_connect(b);
+ else
+ skipped = true;
+
+ if (!skipped) {
+ if (r >= 0) {
+ r = attach_io_events(b);
+ if (r >= 0)
+ return r;
+ }
+
+ b->last_connect_error = -r;
+ }
+
+ r = bus_parse_next_address(b);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return b->last_connect_error ? -b->last_connect_error : -ECONNREFUSED;
+ }
+}
+
+int bus_next_address(sd_bus *b) {
+ assert(b);
+
+ bus_reset_parsed_address(b);
+ return bus_start_address(b);
+}
+
+static int bus_start_fd(sd_bus *b) {
+ struct stat st;
+ int r;
+
+ assert(b);
+ assert(b->input_fd >= 0);
+ assert(b->output_fd >= 0);
+
+ r = fd_nonblock(b->input_fd, true);
+ if (r < 0)
+ return r;
+
+ r = fd_cloexec(b->input_fd, true);
+ if (r < 0)
+ return r;
+
+ if (b->input_fd != b->output_fd) {
+ r = fd_nonblock(b->output_fd, true);
+ if (r < 0)
+ return r;
+
+ r = fd_cloexec(b->output_fd, true);
+ if (r < 0)
+ return r;
+ }
+
+ if (fstat(b->input_fd, &st) < 0)
+ return -errno;
+
+ if (S_ISCHR(b->input_fd))
+ return bus_kernel_take_fd(b);
+ else
+ return bus_socket_take_fd(b);
+}
+
+_public_ int sd_bus_start(sd_bus *bus) {
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ bus->state = BUS_OPENING;
+
+ if (bus->is_server && bus->bus_client)
+ return -EINVAL;
+
+ if (bus->input_fd >= 0)
+ r = bus_start_fd(bus);
+ else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path || bus->kernel || bus->machine)
+ r = bus_start_address(bus);
+ else
+ return -EINVAL;
+
+ if (r < 0)
+ return r;
+
+ return bus_send_hello(bus);
+}
+
+_public_ int sd_bus_open_system(sd_bus **ret) {
+ const char *e;
+ sd_bus *b;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ r = sd_bus_new(&b);
+ if (r < 0)
+ return r;
+
+ e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS");
+ if (e)
+ r = sd_bus_set_address(b, e);
+ else
+ r = sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_PATH);
+ if (r < 0)
+ goto fail;
+
+ b->bus_client = true;
+
+ /* Let's do per-method access control on the system bus. We
+ * need the caller's UID and capability set for that. */
+ b->trusted = false;
+ b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS;
+
+ r = sd_bus_start(b);
+ if (r < 0)
+ goto fail;
+
+ *ret = b;
+ return 0;
+
+fail:
+ bus_free(b);
+ return r;
+}
+
+_public_ int sd_bus_open_user(sd_bus **ret) {
+ const char *e;
+ sd_bus *b;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ r = sd_bus_new(&b);
+ if (r < 0)
+ return r;
+
+ e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
+ if (e) {
+ r = sd_bus_set_address(b, e);
+ if (r < 0)
+ goto fail;
+ } else {
+ e = secure_getenv("XDG_RUNTIME_DIR");
+ if (e) {
+ _cleanup_free_ char *ee = NULL;
+
+ ee = bus_address_escape(e);
+ if (!ee) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+#ifdef ENABLE_KDBUS
+ asprintf(&b->address, KERNEL_USER_BUS_FMT ";" UNIX_USER_BUS_FMT, (unsigned long) getuid(), ee);
+#else
+ asprintf(&b->address, UNIX_USER_BUS_FMT, ee);
+#endif
+ } else {
+#ifdef ENABLE_KDBUS
+ asprintf(&b->address, KERNEL_USER_BUS_FMT, (unsigned long) getuid());
+#else
+ return -ECONNREFUSED;
+#endif
+ }
+
+ if (!b->address) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ b->bus_client = true;
+
+ /* We don't do any per-method access control on the user
+ * bus. */
+ b->trusted = true;
+
+ r = sd_bus_start(b);
+ if (r < 0)
+ goto fail;
+
+ *ret = b;
+ return 0;
+
+fail:
+ bus_free(b);
+ return r;
+}
+
+_public_ int sd_bus_open_system_remote(const char *host, sd_bus **ret) {
+ _cleanup_free_ char *e = NULL;
+ char *p = NULL;
+ sd_bus *bus;
+ int r;
+
+ assert_return(host, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ e = bus_address_escape(host);
+ if (!e)
+ return -ENOMEM;
+
+ p = strjoin("unixexec:path=ssh,argv1=-xT,argv2=", e, ",argv3=systemd-stdio-bridge", NULL);
+ if (!p)
+ return -ENOMEM;
+
+ r = sd_bus_new(&bus);
+ if (r < 0) {
+ free(p);
+ return r;
+ }
+
+ bus->address = p;
+ bus->bus_client = true;
+
+ r = sd_bus_start(bus);
+ if (r < 0) {
+ bus_free(bus);
+ return r;
+ }
+
+ *ret = bus;
+ return 0;
+}
+
+_public_ int sd_bus_open_system_container(const char *machine, sd_bus **ret) {
+ _cleanup_free_ char *e = NULL;
+ sd_bus *bus;
+ char *p;
+ int r;
+
+ assert_return(machine, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(filename_is_safe(machine), -EINVAL);
+
+ e = bus_address_escape(machine);
+ if (!e)
+ return -ENOMEM;
+
+#ifdef ENABLE_KDBUS
+ p = strjoin("x-container-kernel:machine=", e, ";x-container-unix:machine=", e, NULL);
+#else
+ p = strjoin("x-container-unix:machine=", e, NULL);
+#endif
+ if (!p)
+ return -ENOMEM;
+
+ r = sd_bus_new(&bus);
+ if (r < 0) {
+ free(p);
+ return r;
+ }
+
+ bus->address = p;
+ bus->bus_client = true;
+
+ r = sd_bus_start(bus);
+ if (r < 0) {
+ bus_free(bus);
+ return r;
+ }
+
+ *ret = bus;
+ return 0;
+}
+
+_public_ void sd_bus_close(sd_bus *bus) {
+
+ if (!bus)
+ return;
+ if (bus->state == BUS_CLOSED)
+ return;
+ if (bus_pid_changed(bus))
+ return;
+
+ bus->state = BUS_CLOSED;
+
+ sd_bus_detach_event(bus);
+
+ /* Drop all queued messages so that they drop references to
+ * the bus object and the bus may be freed */
+ bus_reset_queues(bus);
+
+ if (!bus->is_kernel)
+ bus_close_fds(bus);
+
+ /* We'll leave the fd open in case this is a kernel bus, since
+ * there might still be memblocks around that reference this
+ * bus, and they might need to invoke the KDBUS_CMD_FREE
+ * ioctl on the fd when they are freed. */
+}
+
+static void bus_enter_closing(sd_bus *bus) {
+ assert(bus);
+
+ if (bus->state != BUS_OPENING &&
+ bus->state != BUS_AUTHENTICATING &&
+ bus->state != BUS_HELLO &&
+ bus->state != BUS_RUNNING)
+ return;
+
+ bus->state = BUS_CLOSING;
+}
+
+_public_ sd_bus *sd_bus_ref(sd_bus *bus) {
+ assert_return(bus, NULL);
+
+ assert_se(REFCNT_INC(bus->n_ref) >= 2);
+
+ return bus;
+}
+
+_public_ sd_bus *sd_bus_unref(sd_bus *bus) {
+ unsigned i;
+
+ if (!bus)
+ return NULL;
+
+ i = REFCNT_DEC(bus->n_ref);
+ if (i != bus->rqueue_size + bus->wqueue_size)
+ return NULL;
+
+ for (i = 0; i < bus->rqueue_size; i++)
+ if (bus->rqueue[i]->n_ref > 1)
+ return NULL;
+
+ for (i = 0; i < bus->wqueue_size; i++)
+ if (bus->wqueue[i]->n_ref > 1)
+ return NULL;
+
+ /* we are the only holders on the messages */
+ bus_free(bus);
+
+ return NULL;
+}
+
+_public_ int sd_bus_is_open(sd_bus *bus) {
+
+ assert_return(bus, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ return BUS_IS_OPEN(bus->state);
+}
+
+_public_ int sd_bus_can_send(sd_bus *bus, char type) {
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus->state != BUS_UNSET, -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (type == SD_BUS_TYPE_UNIX_FD) {
+ if (!(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD))
+ return 0;
+
+ r = bus_ensure_running(bus);
+ if (r < 0)
+ return r;
+
+ return bus->can_fds;
+ }
+
+ return bus_type_is_valid(type);
+}
+
+_public_ int sd_bus_get_server_id(sd_bus *bus, sd_id128_t *server_id) {
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(server_id, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = bus_ensure_running(bus);
+ if (r < 0)
+ return r;
+
+ *server_id = bus->server_id;
+ return 0;
+}
+
+static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) {
+ assert(b);
+ assert(m);
+
+ if (m->sealed) {
+ /* If we copy the same message to multiple
+ * destinations, avoid using the same cookie
+ * numbers. */
+ b->cookie = MAX(b->cookie, BUS_MESSAGE_COOKIE(m));
+ return 0;
+ }
+
+ if (timeout == 0)
+ timeout = BUS_DEFAULT_TIMEOUT;
+
+ return bus_message_seal(m, ++b->cookie, timeout);
+}
+
+static int bus_remarshal_message(sd_bus *b, sd_bus_message **m) {
+ assert(b);
+
+ /* Do packet version and endianess already match? */
+ if ((b->message_version == 0 || b->message_version == (*m)->header->version) &&
+ (b->message_endian == 0 || b->message_endian == (*m)->header->endian))
+ return 0;
+
+ /* No? Then remarshal! */
+ return bus_message_remarshal(b, m);
+}
+
+int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) {
+ assert(b);
+ assert(m);
+
+ /* The bus specification says the serial number cannot be 0,
+ * hence let's fill something in for synthetic messages. Since
+ * synthetic messages might have a fake sender and we don't
+ * want to interfere with the real sender's serial numbers we
+ * pick a fixed, artifical one. We use (uint32_t) -1 rather
+ * than (uint64_t) -1 since dbus1 only had 32bit identifiers,
+ * even though kdbus can do 64bit. */
+
+ return bus_message_seal(m, 0xFFFFFFFFULL, 0);
+}
+
+static int bus_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ if (bus->is_kernel)
+ r = bus_kernel_write_message(bus, m);
+ else
+ r = bus_socket_write_message(bus, m, idx);
+
+ if (r <= 0)
+ return r;
+
+ if (bus->is_kernel || *idx >= BUS_MESSAGE_SIZE(m))
+ log_debug("Sent message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%lu reply_cookie=%lu error=%s",
+ bus_message_type_to_string(m->header->type),
+ strna(sd_bus_message_get_sender(m)),
+ strna(sd_bus_message_get_destination(m)),
+ strna(sd_bus_message_get_path(m)),
+ strna(sd_bus_message_get_interface(m)),
+ strna(sd_bus_message_get_member(m)),
+ (unsigned long) BUS_MESSAGE_COOKIE(m),
+ (unsigned long) m->reply_cookie,
+ strna(m->error.message));
+
+ return r;
+}
+
+static int dispatch_wqueue(sd_bus *bus) {
+ int r, ret = 0;
+
+ assert(bus);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ while (bus->wqueue_size > 0) {
+
+ r = bus_write_message(bus, bus->wqueue[0], &bus->windex);
+ if (r < 0)
+ return r;
+ else if (r == 0)
+ /* Didn't do anything this time */
+ return ret;
+ else if (bus->is_kernel || bus->windex >= BUS_MESSAGE_SIZE(bus->wqueue[0])) {
+ /* Fully written. Let's drop the entry from
+ * the queue.
+ *
+ * This isn't particularly optimized, but
+ * well, this is supposed to be our worst-case
+ * buffer only, and the socket buffer is
+ * supposed to be our primary buffer, and if
+ * it got full, then all bets are off
+ * anyway. */
+
+ bus->wqueue_size --;
+ sd_bus_message_unref(bus->wqueue[0]);
+ memmove(bus->wqueue, bus->wqueue + 1, sizeof(sd_bus_message*) * bus->wqueue_size);
+ bus->windex = 0;
+
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+static int bus_read_message(sd_bus *bus) {
+ assert(bus);
+
+ if (bus->is_kernel)
+ return bus_kernel_read_message(bus);
+ else
+ return bus_socket_read_message(bus);
+}
+
+int bus_rqueue_make_room(sd_bus *bus) {
+ assert(bus);
+
+ if (bus->rqueue_size >= BUS_RQUEUE_MAX)
+ return -ENOBUFS;
+
+ if (!GREEDY_REALLOC(bus->rqueue, bus->rqueue_allocated, bus->rqueue_size + 1))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) {
+ int r, ret = 0;
+
+ assert(bus);
+ assert(m);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ for (;;) {
+ if (bus->rqueue_size > 0) {
+ /* Dispatch a queued message */
+
+ *m = bus->rqueue[0];
+ bus->rqueue_size --;
+ memmove(bus->rqueue, bus->rqueue + 1, sizeof(sd_bus_message*) * bus->rqueue_size);
+ return 1;
+ }
+
+ /* Try to read a new message */
+ r = bus_read_message(bus);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return ret;
+
+ ret = 1;
+ }
+}
+
+_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = sd_bus_message_ref(_m);
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(m, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (m->n_fds > 0) {
+ r = sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOTSUP;
+ }
+
+ /* If the cookie number isn't kept, then we know that no reply
+ * is expected */
+ if (!cookie && !m->sealed)
+ m->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
+
+ r = bus_seal_message(bus, m, 0);
+ if (r < 0)
+ return r;
+
+ /* Remarshall if we have to. This will possibly unref the
+ * message and place a replacement in m */
+ r = bus_remarshal_message(bus, &m);
+ if (r < 0)
+ return r;
+
+ /* If this is a reply and no reply was requested, then let's
+ * suppress this, if we can */
+ if (m->dont_send && !cookie)
+ return 1;
+
+ if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) {
+ size_t idx = 0;
+
+ r = bus_write_message(bus, m, &idx);
+ if (r < 0) {
+ if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ bus_enter_closing(bus);
+ return -ECONNRESET;
+ }
+
+ return r;
+ } else if (!bus->is_kernel && idx < BUS_MESSAGE_SIZE(m)) {
+ /* Wasn't fully written. So let's remember how
+ * much was written. Note that the first entry
+ * of the wqueue array is always allocated so
+ * that we always can remember how much was
+ * written. */
+ bus->wqueue[0] = sd_bus_message_ref(m);
+ bus->wqueue_size = 1;
+ bus->windex = idx;
+ }
+ } else {
+ /* Just append it to the queue. */
+
+ if (bus->wqueue_size >= BUS_WQUEUE_MAX)
+ return -ENOBUFS;
+
+ if (!GREEDY_REALLOC(bus->wqueue, bus->wqueue_allocated, bus->wqueue_size + 1))
+ return -ENOMEM;
+
+ bus->wqueue[bus->wqueue_size ++] = sd_bus_message_ref(m);
+ }
+
+ if (cookie)
+ *cookie = BUS_MESSAGE_COOKIE(m);
+
+ return 1;
+}
+
+_public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie) {
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(m, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (!streq_ptr(m->destination, destination)) {
+
+ if (!destination)
+ return -EEXIST;
+
+ r = sd_bus_message_set_destination(m, destination);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_send(bus, m, cookie);
+}
+
+static usec_t calc_elapse(uint64_t usec) {
+ if (usec == (uint64_t) -1)
+ return 0;
+
+ return now(CLOCK_MONOTONIC) + usec;
+}
+
+static int timeout_compare(const void *a, const void *b) {
+ const struct reply_callback *x = a, *y = b;
+
+ if (x->timeout != 0 && y->timeout == 0)
+ return -1;
+
+ if (x->timeout == 0 && y->timeout != 0)
+ return 1;
+
+ if (x->timeout < y->timeout)
+ return -1;
+
+ if (x->timeout > y->timeout)
+ return 1;
+
+ return 0;
+}
+
+_public_ int sd_bus_call_async(
+ sd_bus *bus,
+ sd_bus_message *_m,
+ sd_bus_message_handler_t callback,
+ void *userdata,
+ uint64_t usec,
+ uint64_t *cookie) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = sd_bus_message_ref(_m);
+ struct reply_callback *c;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(m, -EINVAL);
+ assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
+ assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = hashmap_ensure_allocated(&bus->reply_callbacks, uint64_hash_func, uint64_compare_func);
+ if (r < 0)
+ return r;
+
+ r = prioq_ensure_allocated(&bus->reply_callbacks_prioq, timeout_compare);
+ if (r < 0)
+ return r;
+
+ r = bus_seal_message(bus, m, usec);
+ if (r < 0)
+ return r;
+
+ r = bus_remarshal_message(bus, &m);
+ if (r < 0)
+ return r;
+
+ c = new0(struct reply_callback, 1);
+ if (!c)
+ return -ENOMEM;
+
+ c->callback = callback;
+ c->userdata = userdata;
+ c->cookie = BUS_MESSAGE_COOKIE(m);
+ c->timeout = calc_elapse(m->timeout);
+
+ r = hashmap_put(bus->reply_callbacks, &c->cookie, c);
+ if (r < 0) {
+ free(c);
+ return r;
+ }
+
+ if (c->timeout != 0) {
+ r = prioq_put(bus->reply_callbacks_prioq, c, &c->prioq_idx);
+ if (r < 0) {
+ c->timeout = 0;
+ sd_bus_call_async_cancel(bus, c->cookie);
+ return r;
+ }
+ }
+
+ r = sd_bus_send(bus, m, cookie);
+ if (r < 0) {
+ sd_bus_call_async_cancel(bus, c->cookie);
+ return r;
+ }
+
+ return r;
+}
+
+_public_ int sd_bus_call_async_cancel(sd_bus *bus, uint64_t cookie) {
+ struct reply_callback *c;
+
+ assert_return(bus, -EINVAL);
+ assert_return(cookie != 0, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ c = hashmap_remove(bus->reply_callbacks, &cookie);
+ if (!c)
+ return 0;
+
+ if (c->timeout != 0)
+ prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
+
+ free(c);
+ return 1;
+}
+
+int bus_ensure_running(sd_bus *bus) {
+ int r;
+
+ assert(bus);
+
+ if (bus->state == BUS_UNSET || bus->state == BUS_CLOSED || bus->state == BUS_CLOSING)
+ return -ENOTCONN;
+ if (bus->state == BUS_RUNNING)
+ return 1;
+
+ for (;;) {
+ r = sd_bus_process(bus, NULL);
+ if (r < 0)
+ return r;
+ if (bus->state == BUS_RUNNING)
+ return 1;
+ if (r > 0)
+ continue;
+
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0)
+ return r;
+ }
+}
+
+_public_ int sd_bus_call(
+ sd_bus *bus,
+ sd_bus_message *_m,
+ uint64_t usec,
+ sd_bus_error *error,
+ sd_bus_message **reply) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = sd_bus_message_ref(_m);
+ usec_t timeout;
+ uint64_t cookie;
+ unsigned i;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(m, -EINVAL);
+ assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
+ assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
+ assert_return(!bus_error_is_dirty(error), -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = bus_ensure_running(bus);
+ if (r < 0)
+ return r;
+
+ i = bus->rqueue_size;
+
+ r = bus_seal_message(bus, m, usec);
+ if (r < 0)
+ return r;
+
+ r = bus_remarshal_message(bus, &m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_send(bus, m, &cookie);
+ if (r < 0)
+ return r;
+
+ timeout = calc_elapse(m->timeout);
+
+ for (;;) {
+ usec_t left;
+
+ while (i < bus->rqueue_size) {
+ sd_bus_message *incoming = NULL;
+
+ incoming = bus->rqueue[i];
+
+ if (incoming->reply_cookie == cookie) {
+ /* Found a match! */
+
+ memmove(bus->rqueue + i, bus->rqueue + i + 1, sizeof(sd_bus_message*) * (bus->rqueue_size - i - 1));
+ bus->rqueue_size--;
+
+ if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) {
+
+ if (reply)
+ *reply = incoming;
+ else
+ sd_bus_message_unref(incoming);
+
+ return 1;
+ } else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR)
+ r = sd_bus_error_copy(error, &incoming->error);
+ else
+ r = -EIO;
+
+ sd_bus_message_unref(incoming);
+ return r;
+
+ } else if (BUS_MESSAGE_COOKIE(incoming) == cookie &&
+ bus->unique_name &&
+ incoming->sender &&
+ streq(bus->unique_name, incoming->sender)) {
+
+ memmove(bus->rqueue + i, bus->rqueue + i + 1, sizeof(sd_bus_message*) * (bus->rqueue_size - i - 1));
+ bus->rqueue_size--;
+
+ /* Our own message? Somebody is trying
+ * to send its own client a message,
+ * let's not dead-lock, let's fail
+ * immediately. */
+
+ sd_bus_message_unref(incoming);
+ return -ELOOP;
+ }
+
+ /* Try to read more, right-away */
+ i++;
+ }
+
+ r = bus_read_message(bus);
+ if (r < 0) {
+ if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ bus_enter_closing(bus);
+ return -ECONNRESET;
+ }
+
+ return r;
+ }
+ if (r > 0)
+ continue;
+
+ if (timeout > 0) {
+ usec_t n;
+
+ n = now(CLOCK_MONOTONIC);
+ if (n >= timeout)
+ return -ETIMEDOUT;
+
+ left = timeout - n;
+ } else
+ left = (uint64_t) -1;
+
+ r = bus_poll(bus, true, left);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ETIMEDOUT;
+
+ r = dispatch_wqueue(bus);
+ if (r < 0) {
+ if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ bus_enter_closing(bus);
+ return -ECONNRESET;
+ }
+
+ return r;
+ }
+ }
+}
+
+_public_ int sd_bus_get_fd(sd_bus *bus) {
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus->input_fd == bus->output_fd, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ return bus->input_fd;
+}
+
+_public_ int sd_bus_get_events(sd_bus *bus) {
+ int flags = 0;
+
+ assert_return(bus, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state) || bus->state == BUS_CLOSING, -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (bus->state == BUS_OPENING)
+ flags |= POLLOUT;
+ else if (bus->state == BUS_AUTHENTICATING) {
+
+ if (bus_socket_auth_needs_write(bus))
+ flags |= POLLOUT;
+
+ flags |= POLLIN;
+
+ } else if (bus->state == BUS_RUNNING || bus->state == BUS_HELLO) {
+ if (bus->rqueue_size <= 0)
+ flags |= POLLIN;
+ if (bus->wqueue_size > 0)
+ flags |= POLLOUT;
+ }
+
+ return flags;
+}
+
+_public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
+ struct reply_callback *c;
+
+ assert_return(bus, -EINVAL);
+ assert_return(timeout_usec, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state) || bus->state == BUS_CLOSING, -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (bus->state == BUS_CLOSING) {
+ *timeout_usec = 0;
+ return 1;
+ }
+
+ if (bus->state == BUS_AUTHENTICATING) {
+ *timeout_usec = bus->auth_timeout;
+ return 1;
+ }
+
+ if (bus->state != BUS_RUNNING && bus->state != BUS_HELLO) {
+ *timeout_usec = (uint64_t) -1;
+ return 0;
+ }
+
+ if (bus->rqueue_size > 0) {
+ *timeout_usec = 0;
+ return 1;
+ }
+
+ c = prioq_peek(bus->reply_callbacks_prioq);
+ if (!c) {
+ *timeout_usec = (uint64_t) -1;
+ return 0;
+ }
+
+ *timeout_usec = c->timeout;
+ return 1;
+}
+
+static int process_timeout(sd_bus *bus) {
+ _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message* m = NULL;
+ struct reply_callback *c;
+ usec_t n;
+ int r;
+
+ assert(bus);
+
+ c = prioq_peek(bus->reply_callbacks_prioq);
+ if (!c)
+ return 0;
+
+ n = now(CLOCK_MONOTONIC);
+ if (c->timeout > n)
+ return 0;
+
+ r = bus_message_new_synthetic_error(
+ bus,
+ c->cookie,
+ &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call timed out"),
+ &m);
+ if (r < 0)
+ return r;
+
+ m->sender = "org.freedesktop.DBus";
+
+ r = bus_seal_synthetic_message(bus, m);
+ if (r < 0)
+ return r;
+
+ assert_se(prioq_pop(bus->reply_callbacks_prioq) == c);
+ hashmap_remove(bus->reply_callbacks, &c->cookie);
+
+ bus->current = m;
+ bus->iteration_counter ++;
+
+ r = c->callback(bus, m, c->userdata, &error_buffer);
+ r = bus_maybe_reply_error(m, r, &error_buffer);
+ free(c);
+
+ bus->current = NULL;
+
+ return r;
+}
+
+static int process_hello(sd_bus *bus, sd_bus_message *m) {
+ assert(bus);
+ assert(m);
+
+ if (bus->state != BUS_HELLO)
+ return 0;
+
+ /* Let's make sure the first message on the bus is the HELLO
+ * reply. But note that we don't actually parse the message
+ * here (we leave that to the usual handling), we just verify
+ * we don't let any earlier msg through. */
+
+ if (m->header->type != SD_BUS_MESSAGE_METHOD_RETURN &&
+ m->header->type != SD_BUS_MESSAGE_METHOD_ERROR)
+ return -EIO;
+
+ if (m->reply_cookie != bus->hello_cookie)
+ return -EIO;
+
+ return 0;
+}
+
+static int process_reply(sd_bus *bus, sd_bus_message *m) {
+ _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+ struct reply_callback *c;
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ if (m->header->type != SD_BUS_MESSAGE_METHOD_RETURN &&
+ m->header->type != SD_BUS_MESSAGE_METHOD_ERROR)
+ return 0;
+
+ c = hashmap_remove(bus->reply_callbacks, &m->reply_cookie);
+ if (!c)
+ return 0;
+
+ if (c->timeout != 0)
+ prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ r = c->callback(bus, m, c->userdata, &error_buffer);
+ r = bus_maybe_reply_error(m, r, &error_buffer);
+ free(c);
+
+ return r;
+}
+
+static int process_filter(sd_bus *bus, sd_bus_message *m) {
+ _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+ struct filter_callback *l;
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ do {
+ bus->filter_callbacks_modified = false;
+
+ LIST_FOREACH(callbacks, l, bus->filter_callbacks) {
+
+ if (bus->filter_callbacks_modified)
+ break;
+
+ /* Don't run this more than once per iteration */
+ if (l->last_iteration == bus->iteration_counter)
+ continue;
+
+ l->last_iteration = bus->iteration_counter;
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ r = l->callback(bus, m, l->userdata, &error_buffer);
+ r = bus_maybe_reply_error(m, r, &error_buffer);
+ if (r != 0)
+ return r;
+
+ }
+
+ } while (bus->filter_callbacks_modified);
+
+ return 0;
+}
+
+static int process_match(sd_bus *bus, sd_bus_message *m) {
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ do {
+ bus->match_callbacks_modified = false;
+
+ r = bus_match_run(bus, &bus->match_callbacks, m);
+ if (r != 0)
+ return r;
+
+ } while (bus->match_callbacks_modified);
+
+ return 0;
+}
+
+static int process_builtin(sd_bus *bus, sd_bus_message *m) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ if (bus->manual_peer_interface)
+ return 0;
+
+ if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
+ return 0;
+
+ if (!streq_ptr(m->interface, "org.freedesktop.DBus.Peer"))
+ return 0;
+
+ if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return 1;
+
+ if (streq_ptr(m->member, "Ping"))
+ r = sd_bus_message_new_method_return(m, &reply);
+ else if (streq_ptr(m->member, "GetMachineId")) {
+ sd_id128_t id;
+ char sid[33];
+
+ r = sd_id128_get_machine(&id);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", sd_id128_to_string(id, sid));
+ } else {
+ r = sd_bus_message_new_method_errorf(
+ m, &reply,
+ SD_BUS_ERROR_UNKNOWN_METHOD,
+ "Unknown method '%s' on interface '%s'.", m->member, m->interface);
+ }
+
+ if (r < 0)
+ return r;
+
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int process_message(sd_bus *bus, sd_bus_message *m) {
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ bus->current = m;
+ bus->iteration_counter++;
+
+ log_debug("Got message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%lu reply_cookie=%lu error=%s",
+ bus_message_type_to_string(m->header->type),
+ strna(sd_bus_message_get_sender(m)),
+ strna(sd_bus_message_get_destination(m)),
+ strna(sd_bus_message_get_path(m)),
+ strna(sd_bus_message_get_interface(m)),
+ strna(sd_bus_message_get_member(m)),
+ (unsigned long) BUS_MESSAGE_COOKIE(m),
+ (unsigned long) m->reply_cookie,
+ strna(m->error.message));
+
+ r = process_hello(bus, m);
+ if (r != 0)
+ goto finish;
+
+ r = process_reply(bus, m);
+ if (r != 0)
+ goto finish;
+
+ r = process_filter(bus, m);
+ if (r != 0)
+ goto finish;
+
+ r = process_match(bus, m);
+ if (r != 0)
+ goto finish;
+
+ r = process_builtin(bus, m);
+ if (r != 0)
+ goto finish;
+
+ r = bus_process_object(bus, m);
+
+finish:
+ bus->current = NULL;
+ return r;
+}
+
+static int process_running(sd_bus *bus, sd_bus_message **ret) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ assert(bus);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
+
+ r = process_timeout(bus);
+ if (r != 0)
+ goto null_message;
+
+ r = dispatch_wqueue(bus);
+ if (r != 0)
+ goto null_message;
+
+ r = dispatch_rqueue(bus, &m);
+ if (r < 0)
+ return r;
+ if (!m)
+ goto null_message;
+
+ r = process_message(bus, m);
+ if (r != 0)
+ goto null_message;
+
+ if (ret) {
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ *ret = m;
+ m = NULL;
+ return 1;
+ }
+
+ if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) {
+
+ log_debug("Unprocessed message call sender=%s object=%s interface=%s member=%s",
+ strna(sd_bus_message_get_sender(m)),
+ strna(sd_bus_message_get_path(m)),
+ strna(sd_bus_message_get_interface(m)),
+ strna(sd_bus_message_get_member(m)));
+
+ r = sd_bus_reply_method_errorf(
+ m,
+ SD_BUS_ERROR_UNKNOWN_OBJECT,
+ "Unknown object '%s'.", m->path);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+
+null_message:
+ if (r >= 0 && ret)
+ *ret = NULL;
+
+ return r;
+}
+
+static int process_closing(sd_bus *bus, sd_bus_message **ret) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ struct reply_callback *c;
+ int r;
+
+ assert(bus);
+ assert(bus->state == BUS_CLOSING);
+
+ c = hashmap_first(bus->reply_callbacks);
+ if (c) {
+ _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+
+ /* First, fail all outstanding method calls */
+ r = bus_message_new_synthetic_error(
+ bus,
+ c->cookie,
+ &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Connection terminated"),
+ &m);
+ if (r < 0)
+ return r;
+
+ r = bus_seal_synthetic_message(bus, m);
+ if (r < 0)
+ return r;
+
+ if (c->timeout != 0)
+ prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
+
+ hashmap_remove(bus->reply_callbacks, &c->cookie);
+
+ bus->current = m;
+ bus->iteration_counter++;
+
+ r = c->callback(bus, m, c->userdata, &error_buffer);
+ r = bus_maybe_reply_error(m, r, &error_buffer);
+ free(c);
+
+ goto finish;
+ }
+
+ /* Then, synthesize a Disconnected message */
+ r = sd_bus_message_new_signal(
+ bus,
+ "/org/freedesktop/DBus/Local",
+ "org.freedesktop.DBus.Local",
+ "Disconnected",
+ &m);
+ if (r < 0)
+ return r;
+
+ m->sender = "org.freedesktop.DBus.Local";
+
+ r = bus_seal_synthetic_message(bus, m);
+ if (r < 0)
+ return r;
+
+ sd_bus_close(bus);
+
+ bus->current = m;
+ bus->iteration_counter++;
+
+ r = process_filter(bus, m);
+ if (r != 0)
+ goto finish;
+
+ r = process_match(bus, m);
+ if (r != 0)
+ goto finish;
+
+ if (ret) {
+ *ret = m;
+ m = NULL;
+ }
+
+ r = 1;
+
+finish:
+ bus->current = NULL;
+ return r;
+}
+
+_public_ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
+ BUS_DONT_DESTROY(bus);
+ int r;
+
+ /* Returns 0 when we didn't do anything. This should cause the
+ * caller to invoke sd_bus_wait() before returning the next
+ * time. Returns > 0 when we did something, which possibly
+ * means *ret is filled in with an unprocessed message. */
+
+ assert_return(bus, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ /* We don't allow recursively invoking sd_bus_process(). */
+ assert_return(!bus->current, -EBUSY);
+
+ switch (bus->state) {
+
+ case BUS_UNSET:
+ return -ENOTCONN;
+
+ case BUS_CLOSED:
+ return -ECONNRESET;
+
+ case BUS_OPENING:
+ r = bus_socket_process_opening(bus);
+ if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ bus_enter_closing(bus);
+ r = 1;
+ } else if (r < 0)
+ return r;
+ if (ret)
+ *ret = NULL;
+ return r;
+
+ case BUS_AUTHENTICATING:
+ r = bus_socket_process_authenticating(bus);
+ if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ bus_enter_closing(bus);
+ r = 1;
+ } else if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = NULL;
+
+ return r;
+
+ case BUS_RUNNING:
+ case BUS_HELLO:
+ r = process_running(bus, ret);
+ if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ bus_enter_closing(bus);
+ r = 1;
+
+ if (ret)
+ *ret = NULL;
+ }
+
+ return r;
+
+ case BUS_CLOSING:
+ return process_closing(bus, ret);
+ }
+
+ assert_not_reached("Unknown state");
+}
+
+static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
+ struct pollfd p[2] = {};
+ int r, e, n;
+ struct timespec ts;
+ usec_t m = (usec_t) -1;
+
+ assert(bus);
+
+ if (bus->state == BUS_CLOSING)
+ return 1;
+
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+
+ e = sd_bus_get_events(bus);
+ if (e < 0)
+ return e;
+
+ if (need_more)
+ /* The caller really needs some more data, he doesn't
+ * care about what's already read, or any timeouts
+ * except its own.*/
+ e |= POLLIN;
+ else {
+ usec_t until;
+ /* The caller wants to process if there's something to
+ * process, but doesn't care otherwise */
+
+ r = sd_bus_get_timeout(bus, &until);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ usec_t nw;
+ nw = now(CLOCK_MONOTONIC);
+ m = until > nw ? until - nw : 0;
+ }
+ }
+
+ if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
+ m = timeout_usec;
+
+ p[0].fd = bus->input_fd;
+ if (bus->output_fd == bus->input_fd) {
+ p[0].events = e;
+ n = 1;
+ } else {
+ p[0].events = e & POLLIN;
+ p[1].fd = bus->output_fd;
+ p[1].events = e & POLLOUT;
+ n = 2;
+ }
+
+ r = ppoll(p, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
+ if (r < 0)
+ return -errno;
+
+ return r > 0 ? 1 : 0;
+}
+
+_public_ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
+
+ assert_return(bus, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (bus->state == BUS_CLOSING)
+ return 0;
+
+ assert_return(BUS_IS_OPEN(bus->state) , -ENOTCONN);
+
+ if (bus->rqueue_size > 0)
+ return 0;
+
+ return bus_poll(bus, false, timeout_usec);
+}
+
+_public_ int sd_bus_flush(sd_bus *bus) {
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (bus->state == BUS_CLOSING)
+ return 0;
+
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+
+ r = bus_ensure_running(bus);
+ if (r < 0)
+ return r;
+
+ if (bus->wqueue_size <= 0)
+ return 0;
+
+ for (;;) {
+ r = dispatch_wqueue(bus);
+ if (r < 0) {
+ if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ bus_enter_closing(bus);
+ return -ECONNRESET;
+ }
+
+ return r;
+ }
+
+ if (bus->wqueue_size <= 0)
+ return 0;
+
+ r = bus_poll(bus, false, (uint64_t) -1);
+ if (r < 0)
+ return r;
+ }
+}
+
+_public_ int sd_bus_add_filter(sd_bus *bus,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ struct filter_callback *f;
+
+ assert_return(bus, -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ f = new0(struct filter_callback, 1);
+ if (!f)
+ return -ENOMEM;
+ f->callback = callback;
+ f->userdata = userdata;
+
+ bus->filter_callbacks_modified = true;
+ LIST_PREPEND(callbacks, bus->filter_callbacks, f);
+ return 0;
+}
+
+_public_ int sd_bus_remove_filter(sd_bus *bus,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ struct filter_callback *f;
+
+ assert_return(bus, -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ LIST_FOREACH(callbacks, f, bus->filter_callbacks) {
+ if (f->callback == callback && f->userdata == userdata) {
+ bus->filter_callbacks_modified = true;
+ LIST_REMOVE(callbacks, bus->filter_callbacks, f);
+ free(f);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+_public_ int sd_bus_add_match(sd_bus *bus,
+ const char *match,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ struct bus_match_component *components = NULL;
+ unsigned n_components = 0;
+ uint64_t cookie = 0;
+ int r = 0;
+
+ assert_return(bus, -EINVAL);
+ assert_return(match, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = bus_match_parse(match, &components, &n_components);
+ if (r < 0)
+ goto finish;
+
+ if (bus->bus_client) {
+ cookie = ++bus->match_cookie;
+
+ r = bus_add_match_internal(bus, match, components, n_components, cookie);
+ if (r < 0)
+ goto finish;
+ }
+
+ bus->match_callbacks_modified = true;
+ r = bus_match_add(&bus->match_callbacks, components, n_components, callback, userdata, cookie, NULL);
+ if (r < 0) {
+ if (bus->bus_client)
+ bus_remove_match_internal(bus, match, cookie);
+ }
+
+finish:
+ bus_match_parse_free(components, n_components);
+ return r;
+}
+
+_public_ int sd_bus_remove_match(sd_bus *bus,
+ const char *match,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ struct bus_match_component *components = NULL;
+ unsigned n_components = 0;
+ int r = 0, q = 0;
+ uint64_t cookie = 0;
+
+ assert_return(bus, -EINVAL);
+ assert_return(match, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = bus_match_parse(match, &components, &n_components);
+ if (r < 0)
+ return r;
+
+ bus->match_callbacks_modified = true;
+ r = bus_match_remove(&bus->match_callbacks, components, n_components, callback, userdata, &cookie);
+
+ if (bus->bus_client)
+ q = bus_remove_match_internal(bus, match, cookie);
+
+ bus_match_parse_free(components, n_components);
+
+ return r < 0 ? r : q;
+}
+
+bool bus_pid_changed(sd_bus *bus) {
+ assert(bus);
+
+ /* We don't support people creating a bus connection and
+ * keeping it around over a fork(). Let's complain. */
+
+ return bus->original_pid != getpid();
+}
+
+static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ r = sd_bus_process(bus, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ r = sd_bus_process(bus, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int prepare_callback(sd_event_source *s, void *userdata) {
+ sd_bus *bus = userdata;
+ int r, e;
+ usec_t until;
+
+ assert(s);
+ assert(bus);
+
+ e = sd_bus_get_events(bus);
+ if (e < 0)
+ return e;
+
+ if (bus->output_fd != bus->input_fd) {
+
+ r = sd_event_source_set_io_events(bus->input_io_event_source, e & POLLIN);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_io_events(bus->output_io_event_source, e & POLLOUT);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_event_source_set_io_events(bus->input_io_event_source, e);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_get_timeout(bus, &until);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ int j;
+
+ j = sd_event_source_set_time(bus->time_event_source, until);
+ if (j < 0)
+ return j;
+ }
+
+ r = sd_event_source_set_enabled(bus->time_event_source, r > 0);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int quit_callback(sd_event_source *event, void *userdata) {
+ sd_bus *bus = userdata;
+
+ assert(event);
+
+ sd_bus_flush(bus);
+
+ return 1;
+}
+
+static int attach_io_events(sd_bus *bus) {
+ int r;
+
+ assert(bus);
+
+ if (bus->input_fd < 0)
+ return 0;
+
+ if (!bus->event)
+ return 0;
+
+ if (!bus->input_io_event_source) {
+ r = sd_event_add_io(bus->event, bus->input_fd, 0, io_callback, bus, &bus->input_io_event_source);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_prepare(bus->input_io_event_source, prepare_callback);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(bus->input_io_event_source, bus->event_priority);
+ } else
+ r = sd_event_source_set_io_fd(bus->input_io_event_source, bus->input_fd);
+
+ if (r < 0)
+ return r;
+
+ if (bus->output_fd != bus->input_fd) {
+ assert(bus->output_fd >= 0);
+
+ if (!bus->output_io_event_source) {
+ r = sd_event_add_io(bus->event, bus->output_fd, 0, io_callback, bus, &bus->output_io_event_source);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(bus->output_io_event_source, bus->event_priority);
+ } else
+ r = sd_event_source_set_io_fd(bus->output_io_event_source, bus->output_fd);
+
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static void detach_io_events(sd_bus *bus) {
+ assert(bus);
+
+ if (bus->input_io_event_source) {
+ sd_event_source_set_enabled(bus->input_io_event_source, SD_EVENT_OFF);
+ bus->input_io_event_source = sd_event_source_unref(bus->input_io_event_source);
+ }
+
+ if (bus->output_io_event_source) {
+ sd_event_source_set_enabled(bus->output_io_event_source, SD_EVENT_OFF);
+ bus->output_io_event_source = sd_event_source_unref(bus->output_io_event_source);
+ }
+}
+
+_public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) {
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(!bus->event, -EBUSY);
+
+ assert(!bus->input_io_event_source);
+ assert(!bus->output_io_event_source);
+ assert(!bus->time_event_source);
+
+ if (event)
+ bus->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&bus->event);
+ if (r < 0)
+ return r;
+ }
+
+ bus->event_priority = priority;
+
+ r = sd_event_add_monotonic(bus->event, 0, 0, time_callback, bus, &bus->time_event_source);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_priority(bus->time_event_source, priority);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_add_exit(bus->event, quit_callback, bus, &bus->quit_event_source);
+ if (r < 0)
+ goto fail;
+
+ r = attach_io_events(bus);
+ if (r < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ sd_bus_detach_event(bus);
+ return r;
+}
+
+_public_ int sd_bus_detach_event(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+
+ if (!bus->event)
+ return 0;
+
+ detach_io_events(bus);
+
+ if (bus->time_event_source) {
+ sd_event_source_set_enabled(bus->time_event_source, SD_EVENT_OFF);
+ bus->time_event_source = sd_event_source_unref(bus->time_event_source);
+ }
+
+ if (bus->quit_event_source) {
+ sd_event_source_set_enabled(bus->quit_event_source, SD_EVENT_OFF);
+ bus->quit_event_source = sd_event_source_unref(bus->quit_event_source);
+ }
+
+ if (bus->event)
+ bus->event = sd_event_unref(bus->event);
+
+ return 1;
+}
+
+_public_ sd_event* sd_bus_get_event(sd_bus *bus) {
+ assert_return(bus, NULL);
+
+ return bus->event;
+}
+
+_public_ sd_bus_message* sd_bus_get_current(sd_bus *bus) {
+ assert_return(bus, NULL);
+
+ return bus->current;
+}
+
+static int bus_default(int (*bus_open)(sd_bus **), sd_bus **default_bus, sd_bus **ret) {
+ sd_bus *b = NULL;
+ int r;
+
+ assert(bus_open);
+ assert(default_bus);
+
+ if (!ret)
+ return !!*default_bus;
+
+ if (*default_bus) {
+ *ret = sd_bus_ref(*default_bus);
+ return 0;
+ }
+
+ r = bus_open(&b);
+ if (r < 0)
+ return r;
+
+ b->default_bus_ptr = default_bus;
+ b->tid = gettid();
+ *default_bus = b;
+
+ *ret = b;
+ return 1;
+}
+
+_public_ int sd_bus_default_system(sd_bus **ret) {
+ static thread_local sd_bus *default_system_bus = NULL;
+
+ return bus_default(sd_bus_open_system, &default_system_bus, ret);
+}
+
+_public_ int sd_bus_default_user(sd_bus **ret) {
+ static thread_local sd_bus *default_user_bus = NULL;
+
+ return bus_default(sd_bus_open_user, &default_user_bus, ret);
+}
+
+_public_ int sd_bus_get_tid(sd_bus *b, pid_t *tid) {
+ assert_return(b, -EINVAL);
+ assert_return(tid, -EINVAL);
+ assert_return(!bus_pid_changed(b), -ECHILD);
+
+ if (b->tid != 0) {
+ *tid = b->tid;
+ return 0;
+ }
+
+ if (b->event)
+ return sd_event_get_tid(b->event, tid);
+
+ return -ENXIO;
+}
+
+_public_ char *sd_bus_label_escape(const char *s) {
+ char *r, *t;
+ const char *f;
+
+ assert_return(s, NULL);
+
+ /* Escapes all chars that D-Bus' object path cannot deal
+ * with. Can be reversed with bus_path_unescape(). We special
+ * case the empty string. */
+
+ if (*s == 0)
+ return strdup("_");
+
+ r = new(char, strlen(s)*3 + 1);
+ if (!r)
+ return NULL;
+
+ for (f = s, t = r; *f; f++) {
+
+ /* Escape everything that is not a-zA-Z0-9. We also
+ * escape 0-9 if it's the first character */
+
+ if (!(*f >= 'A' && *f <= 'Z') &&
+ !(*f >= 'a' && *f <= 'z') &&
+ !(f > s && *f >= '0' && *f <= '9')) {
+ *(t++) = '_';
+ *(t++) = hexchar(*f >> 4);
+ *(t++) = hexchar(*f);
+ } else
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return r;
+}
+
+_public_ char *sd_bus_label_unescape(const char *f) {
+ char *r, *t;
+
+ assert_return(f, NULL);
+
+ /* Special case for the empty string */
+ if (streq(f, "_"))
+ return strdup("");
+
+ r = new(char, strlen(f) + 1);
+ if (!r)
+ return NULL;
+
+ for (t = r; *f; f++) {
+
+ if (*f == '_') {
+ int a, b;
+
+ if ((a = unhexchar(f[1])) < 0 ||
+ (b = unhexchar(f[2])) < 0) {
+ /* Invalid escape code, let's take it literal then */
+ *(t++) = '_';
+ } else {
+ *(t++) = (char) ((a << 4) | b);
+ f += 2;
+ }
+ } else
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return r;
+}
+
+_public_ int sd_bus_get_peer_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
+ sd_bus_creds *c;
+ pid_t pid = 0;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP);
+ assert_return(ret, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(!bus->is_kernel, -ENOTSUP);
+
+ if (!bus->ucred_valid && !isempty(bus->label))
+ return -ENODATA;
+
+ c = bus_creds_new();
+ if (!c)
+ return -ENOMEM;
+
+ if (bus->ucred_valid) {
+ pid = c->pid = bus->ucred.pid;
+ c->uid = bus->ucred.uid;
+ c->gid = bus->ucred.gid;
+
+ c->mask |= (SD_BUS_CREDS_UID | SD_BUS_CREDS_PID | SD_BUS_CREDS_GID) & mask;
+ }
+
+ if (!isempty(bus->label) && (mask & SD_BUS_CREDS_SELINUX_CONTEXT)) {
+ c->label = strdup(bus->label);
+ if (!c->label) {
+ sd_bus_creds_unref(c);
+ return -ENOMEM;
+ }
+
+ c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
+ }
+
+ r = bus_creds_add_more(c, mask, pid, 0);
+ if (r < 0)
+ return r;
+
+ *ret = c;
+ return 0;
+}
+
+_public_ int sd_bus_try_close(sd_bus *bus) {
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(bus->is_kernel, -ENOTSUP);
+
+ if (bus->rqueue_size > 0)
+ return -EBUSY;
+
+ if (bus->wqueue_size > 0)
+ return -EBUSY;
+
+ r = bus_kernel_try_close(bus);
+ if (r < 0)
+ return r;
+
+ sd_bus_close(bus);
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/sd-memfd.c b/src/libsystemd/sd-bus/sd-memfd.c
new file mode 100644
index 0000000..112c95f
--- /dev/null
+++ b/src/libsystemd/sd-bus/sd-memfd.c
@@ -0,0 +1,220 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include "util.h"
+#include "kdbus.h"
+
+#include "sd-memfd.h"
+
+struct sd_memfd {
+ int fd;
+ FILE *f;
+};
+
+_public_ int sd_memfd_new(sd_memfd **m) {
+ struct kdbus_cmd_memfd_make cmd = {
+ .size = sizeof(struct kdbus_cmd_memfd_make),
+ };
+ _cleanup_close_ int kdbus = -1;
+ sd_memfd *n;
+
+ assert_return(m, -EINVAL);
+
+ kdbus = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (kdbus < 0)
+ return -errno;
+
+ if (ioctl(kdbus, KDBUS_CMD_MEMFD_NEW, &cmd) < 0)
+ return -errno;
+
+ n = new0(struct sd_memfd, 1);
+ if (!n) {
+ close_nointr_nofail(cmd.fd);
+ return -ENOMEM;
+ }
+
+ n->fd = cmd.fd;
+ *m = n;
+ return 0;
+}
+
+_public_ int sd_memfd_make(int fd, sd_memfd **m) {
+ sd_memfd *n;
+ uint64_t sz;
+
+ assert_return(m, -EINVAL);
+ assert_return(fd >= 0, -EINVAL);
+
+ /* Check if this is a valid memfd */
+ if (ioctl(fd, KDBUS_CMD_MEMFD_SIZE_GET, &sz) < 0)
+ return -ENOTTY;
+
+ n = new0(struct sd_memfd, 1);
+ if (!n)
+ return -ENOMEM;
+
+ n->fd = fd;
+ *m = n;
+
+ return 0;
+}
+
+_public_ void sd_memfd_free(sd_memfd *m) {
+ if (!m)
+ return;
+
+ if (m->f)
+ fclose(m->f);
+ else
+ close_nointr_nofail(m->fd);
+
+ free(m);
+}
+
+_public_ int sd_memfd_get_fd(sd_memfd *m) {
+ assert_return(m, -EINVAL);
+
+ return m->fd;
+}
+
+_public_ int sd_memfd_get_file(sd_memfd *m, FILE **f) {
+ assert_return(m, -EINVAL);
+ assert_return(f, -EINVAL);
+
+ if (!m->f) {
+ m->f = fdopen(m->fd, "r+");
+ if (!m->f)
+ return -errno;
+ }
+
+ *f = m->f;
+ return 0;
+}
+
+_public_ int sd_memfd_dup_fd(sd_memfd *m) {
+ int fd;
+
+ assert_return(m, -EINVAL);
+
+ fd = fcntl(m->fd, F_DUPFD_CLOEXEC, 3);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
+_public_ int sd_memfd_map(sd_memfd *m, uint64_t offset, size_t size, void **p) {
+ void *q;
+ int sealed;
+
+ assert_return(m, -EINVAL);
+ assert_return(size > 0, -EINVAL);
+ assert_return(p, -EINVAL);
+
+ sealed = sd_memfd_get_sealed(m);
+ if (sealed < 0)
+ return sealed;
+
+ q = mmap(NULL, size, sealed ? PROT_READ : PROT_READ|PROT_WRITE, MAP_SHARED, m->fd, offset);
+ if (q == MAP_FAILED)
+ return -errno;
+
+ *p = q;
+ return 0;
+}
+
+_public_ int sd_memfd_set_sealed(sd_memfd *m, int b) {
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ r = ioctl(m->fd, KDBUS_CMD_MEMFD_SEAL_SET, b);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+_public_ int sd_memfd_get_sealed(sd_memfd *m) {
+ int r, b;
+
+ assert_return(m, -EINVAL);
+
+ r = ioctl(m->fd, KDBUS_CMD_MEMFD_SEAL_GET, &b);
+ if (r < 0)
+ return -errno;
+
+ return !!b;
+}
+
+_public_ int sd_memfd_get_size(sd_memfd *m, uint64_t *sz) {
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(sz, -EINVAL);
+
+ r = ioctl(m->fd, KDBUS_CMD_MEMFD_SIZE_GET, sz);
+ if (r < 0)
+ return -errno;
+
+ return r;
+}
+
+_public_ int sd_memfd_set_size(sd_memfd *m, uint64_t sz) {
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ r = ioctl(m->fd, KDBUS_CMD_MEMFD_SIZE_SET, &sz);
+ if (r < 0)
+ return -errno;
+
+ return r;
+}
+
+_public_ int sd_memfd_new_and_map(sd_memfd **m, size_t sz, void **p) {
+ sd_memfd *n;
+ int r;
+
+ r = sd_memfd_new(&n);
+ if (r < 0)
+ return r;
+
+ r = sd_memfd_set_size(n, sz);
+ if (r < 0) {
+ sd_memfd_free(n);
+ return r;
+ }
+
+ r = sd_memfd_map(n, 0, sz, p);
+ if (r < 0) {
+ sd_memfd_free(n);
+ return r;
+ }
+
+ *m = n;
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c
new file mode 100644
index 0000000..021379f
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-chat.c
@@ -0,0 +1,581 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+
+#include "sd-bus.h"
+#include "bus-message.h"
+#include "bus-error.h"
+#include "bus-match.h"
+#include "bus-internal.h"
+#include "bus-util.h"
+
+static int match_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ log_info("Match triggered! interface=%s member=%s", strna(sd_bus_message_get_interface(m)), strna(sd_bus_message_get_member(m)));
+ return 0;
+}
+
+static int object_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ int r;
+
+ assert(bus);
+
+ if (sd_bus_message_is_method_error(m, NULL))
+ return 0;
+
+ if (sd_bus_message_is_method_call(m, "org.object.test", "Foobar")) {
+ log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m));
+
+ r = sd_bus_reply_method_return(m, NULL);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ return r;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int server_init(sd_bus **_bus) {
+ sd_bus *bus = NULL;
+ sd_id128_t id;
+ int r;
+ const char *unique;
+
+ assert(_bus);
+
+ r = sd_bus_open_user(&bus);
+ if (r < 0) {
+ log_error("Failed to connect to user bus: %s", strerror(-r));
+ goto fail;
+ }
+
+ r = sd_bus_get_server_id(bus, &id);
+ if (r < 0) {
+ log_error("Failed to get server ID: %s", strerror(-r));
+ goto fail;
+ }
+
+ r = sd_bus_get_unique_name(bus, &unique);
+ if (r < 0) {
+ log_error("Failed to get unique name: %s", strerror(-r));
+ goto fail;
+ }
+
+ log_info("Peer ID is " SD_ID128_FORMAT_STR ".", SD_ID128_FORMAT_VAL(id));
+ log_info("Unique ID: %s", unique);
+ log_info("Can send file handles: %i", sd_bus_can_send(bus, 'h'));
+
+ r = sd_bus_request_name(bus, "org.freedesktop.systemd.test", 0);
+ if (r < 0) {
+ log_error("Failed to acquire name: %s", strerror(-r));
+ goto fail;
+ }
+
+ r = sd_bus_add_fallback(bus, "/foo/bar", object_callback, NULL);
+ if (r < 0) {
+ log_error("Failed to add object: %s", strerror(-r));
+ goto fail;
+ }
+
+ r = sd_bus_add_match(bus, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL);
+ if (r < 0) {
+ log_error("Failed to add match: %s", strerror(-r));
+ goto fail;
+ }
+
+ r = sd_bus_add_match(bus, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL);
+ if (r < 0) {
+ log_error("Failed to add match: %s", strerror(-r));
+ goto fail;
+ }
+
+ bus_match_dump(&bus->match_callbacks, 0);
+
+ *_bus = bus;
+ return 0;
+
+fail:
+ if (bus)
+ sd_bus_unref(bus);
+
+ return r;
+}
+
+static int server(sd_bus *bus) {
+ int r;
+ bool client1_gone = false, client2_gone = false;
+
+ while (!client1_gone || !client2_gone) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ pid_t pid = 0;
+ const char *label = NULL;
+
+ r = sd_bus_process(bus, &m);
+ if (r < 0) {
+ log_error("Failed to process requests: %s", strerror(-r));
+ goto fail;
+ }
+
+ if (r == 0) {
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0) {
+ log_error("Failed to wait: %s", strerror(-r));
+ goto fail;
+ }
+
+ continue;
+ }
+
+ if (!m)
+ continue;
+
+ sd_bus_creds_get_pid(sd_bus_message_get_creds(m), &pid);
+ sd_bus_creds_get_selinux_context(sd_bus_message_get_creds(m), &label);
+ log_info("Got message! member=%s pid=%lu label=%s",
+ strna(sd_bus_message_get_member(m)),
+ (unsigned long) pid,
+ strna(label));
+ /* bus_message_dump(m); */
+ /* sd_bus_message_rewind(m, true); */
+
+ if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "LowerCase")) {
+ const char *hello;
+ _cleanup_free_ char *lowercase = NULL;
+
+ r = sd_bus_message_read(m, "s", &hello);
+ if (r < 0) {
+ log_error("Failed to get parameter: %s", strerror(-r));
+ goto fail;
+ }
+
+ lowercase = strdup(hello);
+ if (!lowercase) {
+ r = log_oom();
+ goto fail;
+ }
+
+ ascii_strlower(lowercase);
+
+ r = sd_bus_reply_method_return(m, "s", lowercase);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+ } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) {
+
+ r = sd_bus_reply_method_return(m, NULL);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+
+ client1_gone = true;
+ } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient2")) {
+
+ r = sd_bus_reply_method_return(m, NULL);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+
+ client2_gone = true;
+ } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Slow")) {
+
+ sleep(1);
+
+ r = sd_bus_reply_method_return(m, NULL);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+
+ } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "FileDescriptor")) {
+ int fd;
+ static const char x = 'X';
+
+ r = sd_bus_message_read(m, "h", &fd);
+ if (r < 0) {
+ log_error("Failed to get parameter: %s", strerror(-r));
+ goto fail;
+ }
+
+ log_info("Received fd=%d", fd);
+
+ if (write(fd, &x, 1) < 0) {
+ log_error("Failed to write to fd: %m");
+ close_nointr_nofail(fd);
+ goto fail;
+ }
+
+ r = sd_bus_reply_method_return(m, NULL);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+
+ } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
+
+ r = sd_bus_reply_method_error(
+ m,
+ &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method."));
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+ }
+ }
+
+ r = 0;
+
+fail:
+ if (bus) {
+ sd_bus_flush(bus);
+ sd_bus_unref(bus);
+ }
+
+ return r;
+}
+
+static void* client1(void*p) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ sd_bus *bus = NULL;
+ sd_bus_error error = SD_BUS_ERROR_NULL;
+ const char *hello;
+ int r;
+ int pp[2] = { -1, -1 };
+ char x;
+
+ r = sd_bus_open_user(&bus);
+ if (r < 0) {
+ log_error("Failed to connect to user bus: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "LowerCase",
+ &error,
+ &reply,
+ "s",
+ "HELLO");
+ if (r < 0) {
+ log_error("Failed to issue method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_message_read(reply, "s", &hello);
+ if (r < 0) {
+ log_error("Failed to get string: %s", strerror(-r));
+ goto finish;
+ }
+
+ assert(streq(hello, "hello"));
+
+ if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) {
+ log_error("Failed to allocate pipe: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ log_info("Sending fd=%d", pp[1]);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "FileDescriptor",
+ &error,
+ NULL,
+ "h",
+ pp[1]);
+ if (r < 0) {
+ log_error("Failed to issue method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ errno = 0;
+ if (read(pp[0], &x, 1) <= 0) {
+ log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read");
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (bus) {
+ _cleanup_bus_message_unref_ sd_bus_message *q;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "ExitClient1",
+ &q);
+ if (r < 0)
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ else
+ sd_bus_send(bus, q, NULL);
+
+ sd_bus_flush(bus);
+ sd_bus_unref(bus);
+ }
+
+ sd_bus_error_free(&error);
+
+ close_pipe(pp);
+
+ return INT_TO_PTR(r);
+}
+
+static int quit_callback(sd_bus *b, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ bool *x = userdata;
+
+ log_error("Quit callback: %s", strerror(sd_bus_message_get_errno(m)));
+
+ *x = 1;
+ return 1;
+}
+
+static void* client2(void*p) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ sd_bus *bus = NULL;
+ sd_bus_error error = SD_BUS_ERROR_NULL;
+ bool quit = false;
+ const char *mid;
+ int r;
+
+ r = sd_bus_open_user(&bus);
+ if (r < 0) {
+ log_error("Failed to connect to user bus: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/foo/bar/waldo/piep",
+ "org.object.test",
+ "Foobar",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_send(bus, m, NULL);
+ if (r < 0) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
+ goto finish;
+ }
+
+ sd_bus_message_unref(m);
+ m = NULL;
+
+ r = sd_bus_message_new_signal(
+ bus,
+ "/foobar",
+ "foo.bar",
+ "Notify",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate signal: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_send(bus, m, NULL);
+ if (r < 0) {
+ log_error("Failed to issue signal: %s", bus_error_message(&error, -r));
+ goto finish;
+ }
+
+ sd_bus_message_unref(m);
+ m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.DBus.Peer",
+ "GetMachineId",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
+ goto finish;
+ }
+
+ r = sd_bus_message_read(reply, "s", &mid);
+ if (r < 0) {
+ log_error("Failed to parse machine ID: %s", strerror(-r));
+ goto finish;
+ }
+
+ log_info("Machine ID is %s.", mid);
+
+ sd_bus_message_unref(m);
+ m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "Slow",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_call(bus, m, 200 * USEC_PER_MSEC, &error, &reply);
+ if (r < 0)
+ log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
+ else
+ log_info("Slow call succeed.");
+
+ sd_bus_message_unref(m);
+ m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "Slow",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_call_async(bus, m, quit_callback, &quit, 200 * USEC_PER_MSEC, NULL);
+ if (r < 0) {
+ log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
+ goto finish;
+ }
+
+ while (!quit) {
+ r = sd_bus_process(bus, NULL);
+ if (r < 0) {
+ log_error("Failed to process requests: %s", strerror(-r));
+ goto finish;
+ }
+ if (r == 0) {
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0) {
+ log_error("Failed to wait: %s", strerror(-r));
+ goto finish;
+ }
+ }
+ }
+
+ r = 0;
+
+finish:
+ if (bus) {
+ _cleanup_bus_message_unref_ sd_bus_message *q;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "ExitClient2",
+ &q);
+ if (r < 0) {
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ goto finish;
+ }
+
+ sd_bus_send(bus, q, NULL);
+ sd_bus_flush(bus);
+ sd_bus_unref(bus);
+ }
+
+ sd_bus_error_free(&error);
+ return INT_TO_PTR(r);
+}
+
+int main(int argc, char *argv[]) {
+ pthread_t c1, c2;
+ sd_bus *bus;
+ void *p;
+ int q, r;
+
+ r = server_init(&bus);
+ if (r < 0) {
+ log_info("Failed to connect to bus, skipping tests.");
+ return EXIT_TEST_SKIP;
+ }
+
+ log_info("Initialized...");
+
+ r = pthread_create(&c1, NULL, client1, bus);
+ if (r != 0)
+ return EXIT_FAILURE;
+
+ r = pthread_create(&c2, NULL, client2, bus);
+ if (r != 0)
+ return EXIT_FAILURE;
+
+ r = server(bus);
+
+ q = pthread_join(c1, &p);
+ if (q != 0)
+ return EXIT_FAILURE;
+ if (PTR_TO_INT(p) < 0)
+ return EXIT_FAILURE;
+
+ q = pthread_join(c2, &p);
+ if (q != 0)
+ return EXIT_FAILURE;
+ if (PTR_TO_INT(p) < 0)
+ return EXIT_FAILURE;
+
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-cleanup.c b/src/libsystemd/sd-bus/test-bus-cleanup.c
new file mode 100644
index 0000000..d8ddb84
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-cleanup.c
@@ -0,0 +1,80 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Zbigniew JÄdrzejewski-Szmek
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+
+#include "sd-bus.h"
+#include "bus-util.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "refcnt.h"
+
+static void test_bus_new(void) {
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+
+ assert_se(sd_bus_new(&bus) == 0);
+ printf("after new: refcount %u\n", REFCNT_GET(bus->n_ref));
+}
+
+static void test_bus_open(void) {
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+
+ assert_se(sd_bus_open_system(&bus) >= 0);
+ printf("after open: refcount %u\n", REFCNT_GET(bus->n_ref));
+}
+
+static void test_bus_new_method_call(void) {
+ sd_bus *bus = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+
+ assert_se(sd_bus_open_system(&bus) >= 0);
+
+ assert_se(sd_bus_message_new_method_call(bus, "a.service.name", "/an/object/path", "an.interface.name", "AMethodName", &m) >= 0);
+
+ printf("after message_new_method_call: refcount %u\n", REFCNT_GET(bus->n_ref));
+
+ sd_bus_unref(bus);
+ printf("after bus_unref: refcount %u\n", m->n_ref);
+}
+
+static void test_bus_new_signal(void) {
+ sd_bus *bus = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+
+ assert_se(sd_bus_open_system(&bus) >= 0);
+
+ assert_se(sd_bus_message_new_signal(bus, "/an/object/path", "an.interface.name", "Name", &m) >= 0);
+
+ printf("after message_new_signal: refcount %u\n", REFCNT_GET(bus->n_ref));
+
+ sd_bus_unref(bus);
+ printf("after bus_unref: refcount %u\n", m->n_ref);
+}
+
+int main(int argc, char **argv) {
+ log_parse_environment();
+ log_open();
+
+ test_bus_new();
+ test_bus_open();
+ test_bus_new_method_call();
+ test_bus_new_signal();
+}
diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c
new file mode 100644
index 0000000..966b84c
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-creds.c
@@ -0,0 +1,46 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+#include "bus-dump.h"
+#include "bus-util.h"
+#include "util.h"
+
+int main(int argc, char *argv[]) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ int r;
+
+ r = sd_bus_creds_new_from_pid(0, _SD_BUS_CREDS_ALL, &creds);
+ assert_se(r >= 0);
+
+ bus_creds_dump(creds, NULL);
+
+ creds = sd_bus_creds_unref(creds);
+
+ r = sd_bus_creds_new_from_pid(1, _SD_BUS_CREDS_ALL, &creds);
+ if (r != -EACCES) {
+ assert_se(r >= 0);
+ putchar('\n');
+ bus_creds_dump(creds, NULL);
+ }
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c
new file mode 100644
index 0000000..b78be54
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-error.c
@@ -0,0 +1,115 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+#include "bus-error.h"
+#include "bus-util.h"
+
+int main(int argc, char *argv[]) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL, second = SD_BUS_ERROR_NULL;
+ const sd_bus_error const_error = SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "const error");
+ const sd_bus_error temporarily_const_error = {
+ .name = SD_BUS_ERROR_ACCESS_DENIED,
+ .message = "oh! no",
+ ._need_free = -1
+ };
+
+ assert_se(!sd_bus_error_is_set(&error));
+ assert_se(sd_bus_error_set(&error, SD_BUS_ERROR_NOT_SUPPORTED, "xxx") == -ENOTSUP);
+ assert_se(streq(error.name, SD_BUS_ERROR_NOT_SUPPORTED));
+ assert_se(streq(error.message, "xxx"));
+ assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_NOT_SUPPORTED));
+ assert_se(sd_bus_error_get_errno(&error) == ENOTSUP);
+ assert_se(sd_bus_error_is_set(&error));
+ sd_bus_error_free(&error);
+
+ assert_se(!sd_bus_error_is_set(&error));
+ assert_se(sd_bus_error_setf(&error, SD_BUS_ERROR_FILE_NOT_FOUND, "yyy %i", -1) == -ENOENT);
+ assert_se(streq(error.name, SD_BUS_ERROR_FILE_NOT_FOUND));
+ assert_se(streq(error.message, "yyy -1"));
+ assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND));
+ assert_se(sd_bus_error_get_errno(&error) == ENOENT);
+ assert_se(sd_bus_error_is_set(&error));
+
+ assert_se(!sd_bus_error_is_set(&second));
+ assert_se(second._need_free == 0);
+ assert_se(error._need_free > 0);
+ assert_se(sd_bus_error_copy(&second, &error) == -ENOENT);
+ assert_se(second._need_free > 0);
+ assert_se(streq(error.name, second.name));
+ assert_se(streq(error.message, second.message));
+ assert_se(sd_bus_error_get_errno(&second) == ENOENT);
+ assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_FILE_NOT_FOUND));
+ assert_se(sd_bus_error_is_set(&second));
+
+ sd_bus_error_free(&error);
+ sd_bus_error_free(&second);
+
+ assert_se(!sd_bus_error_is_set(&second));
+ assert_se(const_error._need_free == 0);
+ assert_se(sd_bus_error_copy(&second, &const_error) == -EEXIST);
+ assert_se(second._need_free == 0);
+ assert_se(streq(const_error.name, second.name));
+ assert_se(streq(const_error.message, second.message));
+ assert_se(sd_bus_error_get_errno(&second) == EEXIST);
+ assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_FILE_EXISTS));
+ assert_se(sd_bus_error_is_set(&second));
+ sd_bus_error_free(&second);
+
+ assert_se(!sd_bus_error_is_set(&second));
+ assert_se(temporarily_const_error._need_free < 0);
+ assert_se(sd_bus_error_copy(&second, &temporarily_const_error) == -EACCES);
+ assert_se(second._need_free > 0);
+ assert_se(streq(temporarily_const_error.name, second.name));
+ assert_se(streq(temporarily_const_error.message, second.message));
+ assert_se(sd_bus_error_get_errno(&second) == EACCES);
+ assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_ACCESS_DENIED));
+ assert_se(sd_bus_error_is_set(&second));
+
+ assert_se(!sd_bus_error_is_set(&error));
+ assert_se(sd_bus_error_set_const(&error, "System.Error.EUCLEAN", "Hallo") == -EUCLEAN);
+ assert_se(streq(error.name, "System.Error.EUCLEAN"));
+ assert_se(streq(error.message, "Hallo"));
+ assert_se(sd_bus_error_has_name(&error, "System.Error.EUCLEAN"));
+ assert_se(sd_bus_error_get_errno(&error) == EUCLEAN);
+ assert_se(sd_bus_error_is_set(&error));
+ sd_bus_error_free(&error);
+
+ assert_se(!sd_bus_error_is_set(&error));
+ assert_se(sd_bus_error_set_errno(&error, EBUSY) == -EBUSY);
+ assert_se(streq(error.name, "System.Error.EBUSY"));
+ assert_se(streq(error.message, strerror(EBUSY)));
+ assert_se(sd_bus_error_has_name(&error, "System.Error.EBUSY"));
+ assert_se(sd_bus_error_get_errno(&error) == EBUSY);
+ assert_se(sd_bus_error_is_set(&error));
+ sd_bus_error_free(&error);
+
+ assert_se(!sd_bus_error_is_set(&error));
+ assert_se(sd_bus_error_set_errnof(&error, EIO, "Waldi %c", 'X') == -EIO);
+ assert_se(streq(error.name, SD_BUS_ERROR_IO_ERROR));
+ assert_se(streq(error.message, "Waldi X"));
+ assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR));
+ assert_se(sd_bus_error_get_errno(&error) == EIO);
+ assert_se(sd_bus_error_is_set(&error));
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-gvariant.c b/src/libsystemd/sd-bus/test-bus-gvariant.c
new file mode 100644
index 0000000..cb07c96
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-gvariant.c
@@ -0,0 +1,201 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_GLIB
+#include <glib.h>
+#endif
+
+#include "util.h"
+#include "sd-bus.h"
+#include "bus-gvariant.h"
+#include "bus-util.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-dump.h"
+
+static void test_bus_gvariant_is_fixed_size(void) {
+ assert(bus_gvariant_is_fixed_size("") > 0);
+ assert(bus_gvariant_is_fixed_size("()") > 0);
+ assert(bus_gvariant_is_fixed_size("y") > 0);
+ assert(bus_gvariant_is_fixed_size("u") > 0);
+ assert(bus_gvariant_is_fixed_size("b") > 0);
+ assert(bus_gvariant_is_fixed_size("n") > 0);
+ assert(bus_gvariant_is_fixed_size("q") > 0);
+ assert(bus_gvariant_is_fixed_size("i") > 0);
+ assert(bus_gvariant_is_fixed_size("t") > 0);
+ assert(bus_gvariant_is_fixed_size("d") > 0);
+ assert(bus_gvariant_is_fixed_size("s") == 0);
+ assert(bus_gvariant_is_fixed_size("o") == 0);
+ assert(bus_gvariant_is_fixed_size("g") == 0);
+ assert(bus_gvariant_is_fixed_size("h") > 0);
+ assert(bus_gvariant_is_fixed_size("ay") == 0);
+ assert(bus_gvariant_is_fixed_size("v") == 0);
+ assert(bus_gvariant_is_fixed_size("(u)") > 0);
+ assert(bus_gvariant_is_fixed_size("(uuuuy)") > 0);
+ assert(bus_gvariant_is_fixed_size("(uusuuy)") == 0);
+ assert(bus_gvariant_is_fixed_size("a{ss}") == 0);
+ assert(bus_gvariant_is_fixed_size("((u)yyy(b(iiii)))") > 0);
+ assert(bus_gvariant_is_fixed_size("((u)yyy(b(iiivi)))") == 0);
+}
+
+static void test_bus_gvariant_get_size(void) {
+ assert(bus_gvariant_get_size("") == 0);
+ assert(bus_gvariant_get_size("()") == 0);
+ assert(bus_gvariant_get_size("y") == 1);
+ assert(bus_gvariant_get_size("u") == 4);
+ assert(bus_gvariant_get_size("b") == 1);
+ assert(bus_gvariant_get_size("n") == 2);
+ assert(bus_gvariant_get_size("q") == 2);
+ assert(bus_gvariant_get_size("i") == 4);
+ assert(bus_gvariant_get_size("t") == 8);
+ assert(bus_gvariant_get_size("d") == 8);
+ assert(bus_gvariant_get_size("s") < 0);
+ assert(bus_gvariant_get_size("o") < 0);
+ assert(bus_gvariant_get_size("g") < 0);
+ assert(bus_gvariant_get_size("h") == 4);
+ assert(bus_gvariant_get_size("ay") < 0);
+ assert(bus_gvariant_get_size("v") < 0);
+ assert(bus_gvariant_get_size("(u)") == 4);
+ assert(bus_gvariant_get_size("(uuuuy)") == 20);
+ assert(bus_gvariant_get_size("(uusuuy)") < 0);
+ assert(bus_gvariant_get_size("a{ss}") < 0);
+ assert(bus_gvariant_get_size("((u)yyy(b(iiii)))") == 28);
+ assert(bus_gvariant_get_size("((u)yyy(b(iiivi)))") < 0);
+ assert(bus_gvariant_get_size("((b)(t))") == 16);
+ assert(bus_gvariant_get_size("((b)(b)(t))") == 16);
+ assert(bus_gvariant_get_size("(bt)") == 16);
+ assert(bus_gvariant_get_size("((t)(b))") == 16);
+ assert(bus_gvariant_get_size("(tb)") == 16);
+ assert(bus_gvariant_get_size("((b)(b))") == 2);
+ assert(bus_gvariant_get_size("((t)(t))") == 16);
+}
+
+static void test_bus_gvariant_get_alignment(void) {
+ assert(bus_gvariant_get_alignment("") == 1);
+ assert(bus_gvariant_get_alignment("()") == 1);
+ assert(bus_gvariant_get_alignment("y") == 1);
+ assert(bus_gvariant_get_alignment("b") == 1);
+ assert(bus_gvariant_get_alignment("u") == 4);
+ assert(bus_gvariant_get_alignment("s") == 1);
+ assert(bus_gvariant_get_alignment("o") == 1);
+ assert(bus_gvariant_get_alignment("g") == 1);
+ assert(bus_gvariant_get_alignment("v") == 8);
+ assert(bus_gvariant_get_alignment("h") == 4);
+ assert(bus_gvariant_get_alignment("i") == 4);
+ assert(bus_gvariant_get_alignment("t") == 8);
+ assert(bus_gvariant_get_alignment("x") == 8);
+ assert(bus_gvariant_get_alignment("q") == 2);
+ assert(bus_gvariant_get_alignment("n") == 2);
+ assert(bus_gvariant_get_alignment("d") == 8);
+ assert(bus_gvariant_get_alignment("ay") == 1);
+ assert(bus_gvariant_get_alignment("as") == 1);
+ assert(bus_gvariant_get_alignment("au") == 4);
+ assert(bus_gvariant_get_alignment("an") == 2);
+ assert(bus_gvariant_get_alignment("ans") == 2);
+ assert(bus_gvariant_get_alignment("ant") == 8);
+ assert(bus_gvariant_get_alignment("(ss)") == 1);
+ assert(bus_gvariant_get_alignment("(ssu)") == 4);
+ assert(bus_gvariant_get_alignment("a(ssu)") == 4);
+ assert(bus_gvariant_get_alignment("(u)") == 4);
+ assert(bus_gvariant_get_alignment("(uuuuy)") == 4);
+ assert(bus_gvariant_get_alignment("(uusuuy)") == 4);
+ assert(bus_gvariant_get_alignment("a{ss}") == 1);
+ assert(bus_gvariant_get_alignment("((u)yyy(b(iiii)))") == 4);
+ assert(bus_gvariant_get_alignment("((u)yyy(b(iiivi)))") == 8);
+ assert(bus_gvariant_get_alignment("((b)(t))") == 8);
+ assert(bus_gvariant_get_alignment("((b)(b)(t))") == 8);
+ assert(bus_gvariant_get_alignment("(bt)") == 8);
+ assert(bus_gvariant_get_alignment("((t)(b))") == 8);
+ assert(bus_gvariant_get_alignment("(tb)") == 8);
+ assert(bus_gvariant_get_alignment("((b)(b))") == 1);
+ assert(bus_gvariant_get_alignment("((t)(t))") == 8);
+}
+
+static void test_marshal(void) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *n = NULL;
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_free_ void *blob;
+ size_t sz;
+
+ assert_se(sd_bus_open_system(&bus) >= 0);
+ bus->message_version = 2; /* dirty hack to enable gvariant*/
+
+ assert_se(sd_bus_message_new_method_call(bus, "a.service.name", "/an/object/path/which/is/really/really/long/so/that/we/hit/the/eight/bit/boundary/by/quite/some/margin/to/test/this/stuff/that/it/really/works", "an.interface.name", "AMethodName", &m) >= 0);
+
+ assert_se(sd_bus_message_append(m,
+ "a(usv)", 3,
+ 4711, "first-string-parameter", "(st)", "X", (uint64_t) 1111,
+ 4712, "second-string-parameter", "(a(si))", 2, "Y", 5, "Z", 6,
+ 4713, "third-string-parameter", "(uu)", 1, 2) >= 0);
+
+ assert_se(bus_message_seal(m, 4711, 0) >= 0);
+
+#ifdef HAVE_GLIB
+ {
+ GVariant *v;
+ char *t;
+
+#if !defined(GLIB_VERSION_2_36)
+ g_type_init();
+#endif
+
+ v = g_variant_new_from_data(G_VARIANT_TYPE("(yyyyuuua(yv))"), m->header, sizeof(struct bus_header) + BUS_MESSAGE_FIELDS_SIZE(m), false, NULL, NULL);
+ t = g_variant_print(v, TRUE);
+ printf("%s\n", t);
+ g_free(t);
+ g_variant_unref(v);
+
+ v = g_variant_new_from_data(G_VARIANT_TYPE("(a(usv))"), m->body.data, BUS_MESSAGE_BODY_SIZE(m), false, NULL, NULL);
+ t = g_variant_print(v, TRUE);
+ printf("%s\n", t);
+ g_free(t);
+ g_variant_unref(v);
+ }
+#endif
+
+ assert_se(bus_message_dump(m, NULL, true) >= 0);
+
+ assert_se(bus_message_get_blob(m, &blob, &sz) >= 0);
+
+ assert_se(bus_message_from_malloc(NULL, blob, sz, NULL, 0, NULL, NULL, &n) >= 0);
+ blob = NULL;
+
+ assert_se(bus_message_dump(n, NULL, true) >= 0);
+
+ m = sd_bus_message_unref(m);
+
+ assert_se(sd_bus_message_new_method_call(bus, "a.x", "/a/x", "a.x", "Ax", &m) >= 0);
+
+ assert_se(sd_bus_message_append(m, "as", 0) >= 0);
+
+ assert_se(bus_message_seal(m, 4712, 0) >= 0);
+ assert_se(bus_message_dump(m, NULL, true) >= 0);
+}
+
+int main(int argc, char *argv[]) {
+
+ test_bus_gvariant_is_fixed_size();
+ test_bus_gvariant_get_size();
+ test_bus_gvariant_get_alignment();
+ test_marshal();
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-introspect.c b/src/libsystemd/sd-bus/test-bus-introspect.c
new file mode 100644
index 0000000..67b6461
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-introspect.c
@@ -0,0 +1,66 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "log.h"
+#include "bus-introspect.h"
+
+static int prop_get(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+ return -EINVAL;
+}
+
+static int prop_set(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+ return -EINVAL;
+}
+
+static const sd_bus_vtable vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ SD_BUS_METHOD("Hello", "ssas", "a(uu)", NULL, 0),
+ SD_BUS_METHOD("DeprecatedHello", "", "", NULL, SD_BUS_VTABLE_DEPRECATED),
+ SD_BUS_METHOD("DeprecatedHelloNoReply", "", "", NULL, SD_BUS_VTABLE_DEPRECATED|SD_BUS_VTABLE_METHOD_NO_REPLY),
+ SD_BUS_SIGNAL("Wowza", "sss", 0),
+ SD_BUS_SIGNAL("DeprecatedWowza", "ut", SD_BUS_VTABLE_DEPRECATED),
+ SD_BUS_WRITABLE_PROPERTY("AProperty", "s", prop_get, prop_set, 0, 0),
+ SD_BUS_PROPERTY("AReadOnlyDeprecatedProperty", "(ut)", prop_get, 0, SD_BUS_VTABLE_DEPRECATED),
+ SD_BUS_PROPERTY("ChangingProperty", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("Invalidating", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+ SD_BUS_PROPERTY("Constant", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_VTABLE_END
+};
+
+int main(int argc, char *argv[]) {
+ struct introspect intro;
+
+ log_set_max_level(LOG_DEBUG);
+
+ assert_se(introspect_begin(&intro, false) >= 0);
+
+ fprintf(intro.f, " <interface name=\"org.foo\">\n");
+ assert_se(introspect_write_interface(&intro, vtable) >= 0);
+ fputs(" </interface>\n", intro.f);
+
+ fflush(intro.f);
+ fputs(intro.introspection, stdout);
+
+ introspect_free(&intro);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-kernel-benchmark.c b/src/libsystemd/sd-bus/test-bus-kernel-benchmark.c
new file mode 100644
index 0000000..04627d9
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-kernel-benchmark.c
@@ -0,0 +1,303 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <ctype.h>
+#include <sys/wait.h>
+
+#include "util.h"
+#include "log.h"
+#include "time-util.h"
+
+#include "sd-bus.h"
+#include "bus-message.h"
+#include "bus-error.h"
+#include "bus-kernel.h"
+#include "bus-internal.h"
+#include "bus-util.h"
+
+#define MAX_SIZE (4*1024*1024)
+
+static usec_t arg_loop_usec = 100 * USEC_PER_MSEC;
+
+static void server(sd_bus *b, size_t *result) {
+ int r;
+
+ for (;;) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+
+ r = sd_bus_process(b, &m);
+ assert_se(r >= 0);
+
+ if (r == 0)
+ assert_se(sd_bus_wait(b, (usec_t) -1) >= 0);
+ if (!m)
+ continue;
+
+ if (sd_bus_message_is_method_call(m, "benchmark.server", "Ping"))
+ assert_se(sd_bus_reply_method_return(m, NULL) >= 0);
+ else if (sd_bus_message_is_method_call(m, "benchmark.server", "Work")) {
+ const void *p;
+ size_t sz;
+
+ /* Make sure the mmap is mapped */
+ assert_se(sd_bus_message_read_array(m, 'y', &p, &sz) > 0);
+
+ assert_se(sd_bus_reply_method_return(m, NULL) >= 0);
+ } else if (sd_bus_message_is_method_call(m, "benchmark.server", "Exit")) {
+ uint64_t res;
+ assert_se(sd_bus_message_read(m, "t", &res) > 0);
+
+ *result = res;
+ return;
+
+ } else
+ assert_not_reached("Unknown method");
+ }
+}
+
+static void transaction(sd_bus *b, size_t sz) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ uint8_t *p;
+
+ assert_se(sd_bus_message_new_method_call(b, ":1.1", "/", "benchmark.server", "Work", &m) >= 0);
+ assert_se(sd_bus_message_append_array_space(m, 'y', sz, (void**) &p) >= 0);
+
+ memset(p, 0x80, sz);
+
+ assert_se(sd_bus_call(b, m, 0, NULL, &reply) >= 0);
+}
+
+static void client_bisect(const char *address) {
+ _cleanup_bus_message_unref_ sd_bus_message *x = NULL;
+ size_t lsize, rsize, csize;
+ sd_bus *b;
+ int r;
+
+ r = sd_bus_new(&b);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_address(b, address);
+ assert_se(r >= 0);
+
+ r = sd_bus_start(b);
+ assert_se(r >= 0);
+
+ assert_se(sd_bus_call_method(b, ":1.1", "/", "benchmark.server", "Ping", NULL, NULL, NULL) >= 0);
+
+ lsize = 1;
+ rsize = MAX_SIZE;
+
+ printf("SIZE\tCOPY\tMEMFD\n");
+
+ for (;;) {
+ usec_t t;
+ unsigned n_copying, n_memfd;
+
+ csize = (lsize + rsize) / 2;
+
+ if (csize <= lsize)
+ break;
+
+ if (csize <= 0)
+ break;
+
+ printf("%zu\t", csize);
+
+ b->use_memfd = 0;
+
+ t = now(CLOCK_MONOTONIC);
+ for (n_copying = 0;; n_copying++) {
+ transaction(b, csize);
+ if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
+ break;
+ }
+ printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec));
+
+ b->use_memfd = -1;
+
+ t = now(CLOCK_MONOTONIC);
+ for (n_memfd = 0;; n_memfd++) {
+ transaction(b, csize);
+ if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
+ break;
+ }
+ printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec));
+
+ if (n_copying == n_memfd)
+ break;
+
+ if (n_copying > n_memfd)
+ lsize = csize;
+ else
+ rsize = csize;
+ }
+
+ b->use_memfd = 1;
+ assert_se(sd_bus_message_new_method_call(b, ":1.1", "/", "benchmark.server", "Exit", &x) >= 0);
+ assert_se(sd_bus_message_append(x, "t", csize) >= 0);
+ assert_se(sd_bus_send(b, x, NULL) >= 0);
+
+ sd_bus_unref(b);
+}
+
+static void client_chart(const char *address) {
+ _cleanup_bus_message_unref_ sd_bus_message *x = NULL;
+ size_t csize;
+ sd_bus *b;
+ int r;
+
+ r = sd_bus_new(&b);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_address(b, address);
+ assert_se(r >= 0);
+
+ r = sd_bus_start(b);
+ assert_se(r >= 0);
+
+ assert_se(sd_bus_call_method(b, ":1.1", "/", "benchmark.server", "Ping", NULL, NULL, NULL) >= 0);
+
+ printf("SIZE\tCOPY\tMEMFD\n");
+
+ for (csize = 1; csize <= MAX_SIZE; csize *= 2) {
+ usec_t t;
+ unsigned n_copying, n_memfd;
+
+ printf("%zu\t", csize);
+
+ b->use_memfd = 0;
+
+ t = now(CLOCK_MONOTONIC);
+ for (n_copying = 0;; n_copying++) {
+ transaction(b, csize);
+ if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
+ break;
+ }
+
+ printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec));
+
+ b->use_memfd = -1;
+
+ t = now(CLOCK_MONOTONIC);
+ for (n_memfd = 0;; n_memfd++) {
+ transaction(b, csize);
+ if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
+ break;
+ }
+
+ printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec));
+ }
+
+ b->use_memfd = 1;
+ assert_se(sd_bus_message_new_method_call(b, ":1.1", "/", "benchmark.server", "Exit", &x) >= 0);
+ assert_se(sd_bus_message_append(x, "t", csize) >= 0);
+ assert_se(sd_bus_send(b, x, NULL) >= 0);
+
+ sd_bus_unref(b);
+}
+
+int main(int argc, char *argv[]) {
+ enum {
+ MODE_BISECT,
+ MODE_CHART,
+ } mode = MODE_BISECT;
+ int i;
+ _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL;
+ _cleanup_close_ int bus_ref = -1;
+ cpu_set_t cpuset;
+ size_t result;
+ sd_bus *b;
+ pid_t pid;
+ int r;
+
+ for (i = 1; i < argc; i++) {
+ if (streq(argv[i], "chart")) {
+ mode = MODE_CHART;
+ continue;
+ }
+
+ assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0);
+ }
+
+ assert_se(arg_loop_usec > 0);
+
+ assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
+
+ bus_ref = bus_kernel_create_bus(name, false, &bus_name);
+ if (bus_ref == -ENOENT)
+ exit(EXIT_TEST_SKIP);
+
+ assert_se(bus_ref >= 0);
+
+ address = strappend("kernel:path=", bus_name);
+ assert_se(address);
+
+ r = sd_bus_new(&b);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_address(b, address);
+ assert_se(r >= 0);
+
+ r = sd_bus_start(b);
+ assert_se(r >= 0);
+
+ sync();
+ setpriority(PRIO_PROCESS, 0, -19);
+
+ pid = fork();
+ assert_se(pid >= 0);
+
+ if (pid == 0) {
+ CPU_ZERO(&cpuset);
+ CPU_SET(0, &cpuset);
+ pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
+
+ close_nointr_nofail(bus_ref);
+ sd_bus_unref(b);
+
+ switch (mode) {
+ case MODE_BISECT:
+ client_bisect(address);
+ break;
+
+ case MODE_CHART:
+ client_chart(address);
+ break;
+ }
+
+ _exit(0);
+ }
+
+ CPU_ZERO(&cpuset);
+ CPU_SET(1, &cpuset);
+ pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
+
+ server(b, &result);
+
+ if (mode == MODE_BISECT)
+ printf("Copying/memfd are equally fast at %zu bytes\n", result);
+
+ assert_se(waitpid(pid, NULL, 0) == pid);
+
+ sd_bus_unref(b);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c
new file mode 100644
index 0000000..0ecad18
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c
@@ -0,0 +1,115 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "log.h"
+
+#include "sd-bus.h"
+#include "bus-message.h"
+#include "bus-error.h"
+#include "bus-kernel.h"
+#include "bus-util.h"
+
+static void test_one(
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *arg0,
+ const char *match,
+ bool good) {
+
+ _cleanup_close_ int bus_ref = -1;
+ _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ sd_bus *a, *b;
+ int r;
+
+ assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
+
+ bus_ref = bus_kernel_create_bus(name, false, &bus_name);
+ if (bus_ref == -ENOENT)
+ exit(EXIT_TEST_SKIP);
+
+ assert_se(bus_ref >= 0);
+
+ address = strappend("kernel:path=", bus_name);
+ assert_se(address);
+
+ r = sd_bus_new(&a);
+ assert_se(r >= 0);
+
+ r = sd_bus_new(&b);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_address(a, address);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_address(b, address);
+ assert_se(r >= 0);
+
+ r = sd_bus_start(a);
+ assert_se(r >= 0);
+
+ r = sd_bus_start(b);
+ assert_se(r >= 0);
+
+ log_debug("match");
+ r = sd_bus_add_match(b, match, NULL, NULL);
+ assert_se(r >= 0);
+
+ log_debug("signal");
+ r = sd_bus_emit_signal(a, path, interface, member, "s", arg0);
+ assert_se(r >= 0);
+
+ r = sd_bus_process(b, &m);
+ assert_se(r >= 0 && (good == !!m));
+
+ sd_bus_unref(a);
+ sd_bus_unref(b);
+}
+
+int main(int argc, char *argv[]) {
+ log_set_max_level(LOG_DEBUG);
+
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "", true);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo'", true);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo/tuut'", false);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "interface='waldo.com'", true);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "member='Piep'", true);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "member='Pi_ep'", false);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "arg0='foobar'", true);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "arg0='foo_bar'", false);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar'", true);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar2'", false);
+
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo'", true);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar'", false);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo'", false);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/'", false);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo/quux'", false);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/foo/bar/waldo'", true);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/foo/bar'", true);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/foo'", true);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/'", true);
+ test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/quux'", false);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-kernel.c b/src/libsystemd/sd-bus/test-bus-kernel.c
new file mode 100644
index 0000000..9b17a35
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-kernel.c
@@ -0,0 +1,175 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+
+#include "util.h"
+#include "log.h"
+
+#include "sd-bus.h"
+#include "bus-message.h"
+#include "bus-error.h"
+#include "bus-kernel.h"
+#include "bus-util.h"
+#include "bus-dump.h"
+
+int main(int argc, char *argv[]) {
+ _cleanup_close_ int bus_ref = -1;
+ _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ const char *ua = NULL, *ub = NULL, *the_string = NULL;
+ sd_bus *a, *b;
+ int r, pipe_fds[2];
+
+ log_set_max_level(LOG_DEBUG);
+
+ assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
+
+ bus_ref = bus_kernel_create_bus(name, false, &bus_name);
+ if (bus_ref == -ENOENT)
+ return EXIT_TEST_SKIP;
+
+ assert_se(bus_ref >= 0);
+
+ address = strappend("kernel:path=", bus_name);
+ assert_se(address);
+
+ r = sd_bus_new(&a);
+ assert_se(r >= 0);
+
+ r = sd_bus_new(&b);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_address(a, address);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_address(b, address);
+ assert_se(r >= 0);
+
+ assert_se(sd_bus_negotiate_attach_timestamp(a, 1) >= 0);
+ assert_se(sd_bus_negotiate_attach_creds(a, _SD_BUS_CREDS_ALL) >= 0);
+
+ assert_se(sd_bus_negotiate_attach_timestamp(b, 1) >= 0);
+ assert_se(sd_bus_negotiate_attach_creds(b, _SD_BUS_CREDS_ALL) >= 0);
+
+ r = sd_bus_start(a);
+ assert_se(r >= 0);
+
+ r = sd_bus_start(b);
+ assert_se(r >= 0);
+
+ r = sd_bus_get_unique_name(a, &ua);
+ assert_se(r >= 0);
+
+ printf("unique a: %s\n", ua);
+
+ r = sd_bus_get_unique_name(b, &ub);
+ assert_se(r >= 0);
+
+ printf("unique b: %s\n", ub);
+
+ r = sd_bus_call_method(a, "this.doesnt.exist", "/foo", "meh.mah", "muh", &error, NULL, "s", "yayayay");
+ assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_SERVICE_UNKNOWN));
+ assert_se(r == -EHOSTUNREACH);
+
+ r = sd_bus_add_match(b, "interface='waldo.com',member='Piep'", NULL, NULL);
+ assert_se(r >= 0);
+
+ r = sd_bus_emit_signal(a, "/foo/bar/waldo", "waldo.com", "Piep", "sss", "I am a string", "/this/is/a/path", "and.this.a.domain.name");
+ assert_se(r >= 0);
+
+ r = sd_bus_try_close(b);
+ assert_se(r == -EBUSY);
+
+ r = sd_bus_process(b, &m);
+ assert_se(r > 0);
+ assert_se(m);
+
+ bus_message_dump(m, stdout, true);
+ assert_se(sd_bus_message_rewind(m, true) >= 0);
+
+ r = sd_bus_message_read(m, "s", &the_string);
+ assert_se(r >= 0);
+ assert_se(streq(the_string, "I am a string"));
+
+ sd_bus_message_unref(m);
+ m = NULL;
+
+ r = sd_bus_request_name(a, "net.x0pointer.foobar", 0);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_new_method_call(b, "net.x0pointer.foobar", "/a/path", "an.inter.face", "AMethod", &m);
+ assert_se(r >= 0);
+
+ assert_se(pipe2(pipe_fds, O_CLOEXEC) >= 0);
+
+ assert_se(write(pipe_fds[1], "x", 1) == 1);
+
+ close_nointr_nofail(pipe_fds[1]);
+ pipe_fds[1] = -1;
+
+ r = sd_bus_message_append(m, "h", pipe_fds[0]);
+ assert_se(r >= 0);
+
+ close_nointr_nofail(pipe_fds[0]);
+ pipe_fds[0] = -1;
+
+ r = sd_bus_send(b, m, NULL);
+ assert_se(r >= 0);
+
+ for (;;) {
+ sd_bus_message_unref(m);
+ m = NULL;
+ r = sd_bus_process(a, &m);
+ assert_se(r > 0);
+ assert_se(m);
+
+ bus_message_dump(m, stdout, true);
+ assert_se(sd_bus_message_rewind(m, true) >= 0);
+
+ if (sd_bus_message_is_method_call(m, "an.inter.face", "AMethod")) {
+ int fd;
+ char x;
+
+ r = sd_bus_message_read(m, "h", &fd);
+ assert_se(r >= 0);
+
+ assert_se(read(fd, &x, 1) == 1);
+ assert_se(x == 'x');
+ break;
+ }
+ }
+
+ r = sd_bus_release_name(a, "net.x0pointer.foobar");
+ assert_se(r >= 0);
+
+ r = sd_bus_release_name(a, "net.x0pointer.foobar");
+ assert_se(r == -ESRCH);
+
+ r = sd_bus_try_close(a);
+ assert_se(r >= 0);
+
+ sd_bus_unref(a);
+ sd_bus_unref(b);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c
new file mode 100644
index 0000000..4fad049
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-marshal.c
@@ -0,0 +1,324 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <byteswap.h>
+
+#ifdef HAVE_GLIB
+#include <gio/gio.h>
+#endif
+
+#ifdef HAVE_DBUS
+#include <dbus/dbus.h>
+#endif
+
+#include "log.h"
+#include "util.h"
+
+#include "sd-bus.h"
+#include "bus-message.h"
+#include "bus-util.h"
+#include "bus-dump.h"
+
+static void test_bus_label_escape_one(const char *a, const char *b) {
+ _cleanup_free_ char *t = NULL, *x = NULL, *y = NULL;
+
+ assert_se(t = sd_bus_label_escape(a));
+ assert_se(streq(t, b));
+
+ assert_se(x = sd_bus_label_unescape(t));
+ assert_se(streq(a, x));
+
+ assert_se(y = sd_bus_label_unescape(b));
+ assert_se(streq(a, y));
+}
+
+static void test_bus_label_escape(void) {
+ test_bus_label_escape_one("foo123bar", "foo123bar");
+ test_bus_label_escape_one("foo.bar", "foo_2ebar");
+ test_bus_label_escape_one("foo_2ebar", "foo_5f2ebar");
+ test_bus_label_escape_one("", "_");
+ test_bus_label_escape_one("_", "_5f");
+ test_bus_label_escape_one("1", "_31");
+ test_bus_label_escape_one(":1", "_3a1");
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *copy = NULL;
+ int r, boolean;
+ const char *x, *x2, *y, *z, *a, *b, *c, *d, *a_signature;
+ uint8_t u, v;
+ void *buffer = NULL;
+ size_t sz;
+ char *h;
+ const int32_t integer_array[] = { -1, -2, 0, 1, 2 }, *return_array;
+ char *s;
+ _cleanup_free_ char *first = NULL, *second = NULL, *third = NULL;
+ _cleanup_fclose_ FILE *ms = NULL;
+ size_t first_size = 0, second_size = 0, third_size = 0;
+
+ r = sd_bus_message_new_method_call(NULL, "foobar.waldo", "/", "foobar.waldo", "Piep", &m);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append(m, "s", "a string");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append(m, "s", NULL);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append(m, "asg", 2, "string #1", "string #2", "sba(tt)ss");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append(m, "sass", "foobar", 5, "foo", "bar", "waldo", "piep", "pap", "after");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append(m, "a{yv}", 2, 3, "s", "foo", 5, "s", "waldo");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append(m, "ba(ss)", 255, 3, "aaa", "1", "bbb", "2", "ccc", "3");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append_basic(m, 's', "foobar");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append_basic(m, 's', "waldo");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_close_container(m);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append_string_space(m, 5, &s);
+ assert_se(r >= 0);
+ strcpy(s, "hallo");
+
+ r = sd_bus_message_append_array(m, 'i', integer_array, sizeof(integer_array));
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append_array(m, 'u', NULL, 0);
+ assert_se(r >= 0);
+
+ r = bus_message_seal(m, 4711, 0);
+ assert_se(r >= 0);
+
+ bus_message_dump(m, stdout, true);
+
+ ms = open_memstream(&first, &first_size);
+ bus_message_dump(m, ms, false);
+ fflush(ms);
+ assert_se(!ferror(ms));
+
+ r = bus_message_get_blob(m, &buffer, &sz);
+ assert_se(r >= 0);
+
+ h = hexmem(buffer, sz);
+ assert_se(h);
+
+ log_info("message size = %lu, contents =\n%s", (unsigned long) sz, h);
+ free(h);
+
+#ifdef HAVE_GLIB
+ {
+ GDBusMessage *g;
+ char *p;
+
+#if !defined(GLIB_VERSION_2_36)
+ g_type_init();
+#endif
+
+ g = g_dbus_message_new_from_blob(buffer, sz, 0, NULL);
+ p = g_dbus_message_print(g, 0);
+ log_info("%s", p);
+ g_free(p);
+ g_object_unref(g);
+ }
+#endif
+
+#ifdef HAVE_DBUS
+ {
+ DBusMessage *w;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ w = dbus_message_demarshal(buffer, sz, &error);
+ if (!w)
+ log_error("%s", error.message);
+ else
+ dbus_message_unref(w);
+ }
+#endif
+
+ m = sd_bus_message_unref(m);
+
+ r = bus_message_from_malloc(NULL, buffer, sz, NULL, 0, NULL, NULL, &m);
+ assert_se(r >= 0);
+
+ bus_message_dump(m, stdout, true);
+
+ fclose(ms);
+ ms = open_memstream(&second, &second_size);
+ bus_message_dump(m, ms, false);
+ fflush(ms);
+ assert_se(!ferror(ms));
+ assert_se(first_size == second_size);
+ assert_se(memcmp(first, second, first_size) == 0);
+
+ assert_se(sd_bus_message_rewind(m, true) >= 0);
+
+ r = sd_bus_message_read(m, "ssasg", &x, &x2, 2, &y, &z, &a_signature);
+ assert_se(r > 0);
+ assert_se(streq(x, "a string"));
+ assert_se(streq(x2, ""));
+ assert_se(streq(y, "string #1"));
+ assert_se(streq(z, "string #2"));
+ assert_se(streq(a_signature, "sba(tt)ss"));
+
+ r = sd_bus_message_read(m, "sass", &x, 5, &y, &z, &a, &b, &c, &d);
+ assert_se(r > 0);
+ assert_se(streq(x, "foobar"));
+ assert_se(streq(y, "foo"));
+ assert_se(streq(z, "bar"));
+ assert_se(streq(a, "waldo"));
+ assert_se(streq(b, "piep"));
+ assert_se(streq(c, "pap"));
+ assert_se(streq(d, "after"));
+
+ r = sd_bus_message_read(m, "a{yv}", 2, &u, "s", &x, &v, "s", &y);
+ assert_se(r > 0);
+ assert_se(u == 3);
+ assert_se(streq(x, "foo"));
+ assert_se(v == 5);
+ assert_se(streq(y, "waldo"));
+
+ r = sd_bus_message_read(m, "ba(ss)", &boolean, 3, &x, &y, &a, &b, &c, &d);
+ assert_se(r > 0);
+ assert_se(boolean);
+ assert_se(streq(x, "aaa"));
+ assert_se(streq(y, "1"));
+ assert_se(streq(a, "bbb"));
+ assert_se(streq(b, "2"));
+ assert_se(streq(c, "ccc"));
+ assert_se(streq(d, "3"));
+
+ assert_se(sd_bus_message_verify_type(m, 'a', "s") > 0);
+
+ r = sd_bus_message_read(m, "as", 2, &x, &y);
+ assert_se(r > 0);
+ assert_se(streq(x, "foobar"));
+ assert_se(streq(y, "waldo"));
+
+ r = sd_bus_message_read_basic(m, 's', &s);
+ assert_se(r > 0);
+ assert_se(streq(s, "hallo"));
+
+ r = sd_bus_message_read_array(m, 'i', (const void**) &return_array, &sz);
+ assert_se(r > 0);
+ assert_se(sz == sizeof(integer_array));
+ assert_se(memcmp(integer_array, return_array, sz) == 0);
+
+ r = sd_bus_message_read_array(m, 'u', (const void**) &return_array, &sz);
+ assert_se(r > 0);
+ assert_se(sz == 0);
+
+ r = sd_bus_message_peek_type(m, NULL, NULL);
+ assert_se(r == 0);
+
+ r = sd_bus_message_new_method_call(NULL, "foobar.waldo", "/", "foobar.waldo", "Piep", ©);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_rewind(m, true);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_copy(copy, m, true);
+ assert_se(r >= 0);
+
+ r = bus_message_seal(copy, 4712, 0);
+ assert_se(r >= 0);
+
+ fclose(ms);
+ ms = open_memstream(&third, &third_size);
+ bus_message_dump(copy, ms, false);
+ fflush(ms);
+ assert_se(!ferror(ms));
+
+ printf("<%.*s>\n", (int) first_size, first);
+ printf("<%.*s>\n", (int) third_size, third);
+
+ assert_se(first_size == third_size);
+ assert_se(memcmp(first, third, third_size) == 0);
+
+ r = sd_bus_message_rewind(m, true);
+ assert_se(r >= 0);
+
+ assert_se(sd_bus_message_verify_type(m, 's', NULL) > 0);
+
+ r = sd_bus_message_skip(m, "ssasg");
+ assert_se(r > 0);
+
+ assert_se(sd_bus_message_verify_type(m, 's', NULL) > 0);
+
+ r = sd_bus_message_skip(m, "sass");
+ assert_se(r >= 0);
+
+ assert_se(sd_bus_message_verify_type(m, 'a', "{yv}") > 0);
+
+ r = sd_bus_message_skip(m, "a{yv}");
+ assert_se(r >= 0);
+
+ assert_se(sd_bus_message_verify_type(m, 'b', NULL) > 0);
+
+ r = sd_bus_message_read(m, "b", &boolean);
+ assert_se(r > 0);
+ assert_se(boolean);
+
+ r = sd_bus_message_enter_container(m, 0, NULL);
+ assert_se(r > 0);
+
+ r = sd_bus_message_read(m, "(ss)", &x, &y);
+ assert_se(r > 0);
+
+ r = sd_bus_message_read(m, "(ss)", &a, &b);
+ assert_se(r > 0);
+
+ r = sd_bus_message_read(m, "(ss)", &c, &d);
+ assert_se(r > 0);
+
+ r = sd_bus_message_read(m, "(ss)", &x, &y);
+ assert_se(r == 0);
+
+ r = sd_bus_message_exit_container(m);
+ assert_se(r >= 0);
+
+ assert_se(streq(x, "aaa"));
+ assert_se(streq(y, "1"));
+ assert_se(streq(a, "bbb"));
+ assert_se(streq(b, "2"));
+ assert_se(streq(c, "ccc"));
+ assert_se(streq(d, "3"));
+
+ test_bus_label_escape();
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-match.c b/src/libsystemd/sd-bus/test-bus-match.c
new file mode 100644
index 0000000..7227e25
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-match.c
@@ -0,0 +1,145 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+
+#include "bus-match.h"
+#include "bus-message.h"
+#include "bus-util.h"
+
+static bool mask[32];
+
+static int filter(sd_bus *b, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ log_info("Ran %i", PTR_TO_INT(userdata));
+ mask[PTR_TO_INT(userdata)] = true;
+ return 0;
+}
+
+static bool mask_contains(unsigned a[], unsigned n) {
+ unsigned i, j;
+
+ for (i = 0; i < ELEMENTSOF(mask); i++) {
+ bool found = false;
+
+ for (j = 0; j < n; j++)
+ if (a[j] == i) {
+ found = true;
+ break;
+ }
+
+ if (found != mask[i])
+ return false;
+ }
+
+ return true;
+}
+
+static int match_add(struct bus_match_node *root, const char *match, int value) {
+ struct bus_match_component *components = NULL;
+ unsigned n_components = 0;
+ int r;
+
+ r = bus_match_parse(match, &components, &n_components);
+ if (r < 0)
+ return r;
+
+ r = bus_match_add(root, components, n_components, filter, INT_TO_PTR(value), 0, NULL);
+ bus_match_parse_free(components, n_components);
+
+ return r;
+}
+
+static int match_remove(struct bus_match_node *root, const char *match, int value) {
+ struct bus_match_component *components = NULL;
+ unsigned n_components = 0;
+ int r;
+
+ r = bus_match_parse(match, &components, &n_components);
+ if (r < 0)
+ return r;
+
+ r = bus_match_remove(root, components, n_components, filter, INT_TO_PTR(value), 0);
+ bus_match_parse_free(components, n_components);
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ struct bus_match_node root;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ enum bus_match_node_type i;
+
+ zero(root);
+ root.type = BUS_MATCH_ROOT;
+
+ assert_se(match_add(&root, "arg2='wal\\'do',sender='foo',type='signal',interface='bar.x',", 1) >= 0);
+ assert_se(match_add(&root, "arg2='wal\\'do2',sender='foo',type='signal',interface='bar.x',", 2) >= 0);
+ assert_se(match_add(&root, "arg3='test',sender='foo',type='signal',interface='bar.x',", 3) >= 0);
+ assert_se(match_add(&root, "arg3='test',sender='foo',type='method_call',interface='bar.x',", 4) >= 0);
+ assert_se(match_add(&root, "", 5) >= 0);
+ assert_se(match_add(&root, "interface='quux.x'", 6) >= 0);
+ assert_se(match_add(&root, "interface='bar.x'", 7) >= 0);
+ assert_se(match_add(&root, "member='waldo',path='/foo/bar'", 8) >= 0);
+ assert_se(match_add(&root, "path='/foo/bar'", 9) >= 0);
+ assert_se(match_add(&root, "path_namespace='/foo'", 10) >= 0);
+ assert_se(match_add(&root, "path_namespace='/foo/quux'", 11) >= 0);
+ assert_se(match_add(&root, "arg1='two'", 12) >= 0);
+ assert_se(match_add(&root, "member='waldo',arg2path='/prefix/'", 13) >= 0);
+ assert_se(match_add(&root, "member=waldo,path='/foo/bar',arg3namespace='prefix'", 14) >= 0);
+
+ bus_match_dump(&root, 0);
+
+ assert_se(sd_bus_message_new_signal(NULL, "/foo/bar", "bar.x", "waldo", &m) >= 0);
+ assert_se(sd_bus_message_append(m, "ssss", "one", "two", "/prefix/three", "prefix.four") >= 0);
+ assert_se(bus_message_seal(m, 1, 0) >= 0);
+
+ zero(mask);
+ assert_se(bus_match_run(NULL, &root, m) == 0);
+ assert_se(mask_contains((unsigned[]) { 9, 8, 7, 5, 10, 12, 13, 14 }, 8));
+
+ assert_se(match_remove(&root, "member='waldo',path='/foo/bar'", 8) > 0);
+ assert_se(match_remove(&root, "arg2path='/prefix/',member='waldo'", 13) > 0);
+ assert_se(match_remove(&root, "interface='bar.xx'", 7) == 0);
+
+ bus_match_dump(&root, 0);
+
+ zero(mask);
+ assert_se(bus_match_run(NULL, &root, m) == 0);
+ assert_se(mask_contains((unsigned[]) { 9, 5, 10, 12, 14, 7 }, 6));
+
+ for (i = 0; i < _BUS_MATCH_NODE_TYPE_MAX; i++) {
+ char buf[32];
+ const char *x;
+
+ assert_se(x = bus_match_node_type_to_string(i, buf, sizeof(buf)));
+
+ if (i >= BUS_MATCH_MESSAGE_TYPE)
+ assert_se(bus_match_node_type_from_string(x, strlen(x)) == i);
+ }
+
+ bus_match_free(&root);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-memfd.c b/src/libsystemd/sd-bus/test-bus-memfd.c
new file mode 100644
index 0000000..b9d6a25
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-memfd.c
@@ -0,0 +1,174 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mman.h>
+#include <sys/uio.h>
+
+#include "log.h"
+#include "macro.h"
+#include "util.h"
+
+#include "sd-memfd.h"
+
+int main(int argc, char *argv[]) {
+ sd_memfd *m;
+ char *s;
+ uint64_t sz;
+ int r, fd;
+ FILE *f;
+ char buf[3] = {};
+ struct iovec iov[3] = {};
+ char bufv[3][3] = {};
+
+ log_set_max_level(LOG_DEBUG);
+
+ r = sd_memfd_new(&m);
+ if (r == -ENOENT)
+ return EXIT_TEST_SKIP;
+
+ r = sd_memfd_map(m, 0, 12, (void**) &s);
+ assert_se(r >= 0);
+
+ strcpy(s, "----- world");
+
+ r = sd_memfd_set_sealed(m, 1);
+ assert_se(r == -ETXTBSY);
+
+ assert_se(write(sd_memfd_get_fd(m), "he", 2) == 2);
+ assert_se(write(sd_memfd_get_fd(m), "XXX", 3) == 3);
+ assert_se(streq(s, "heXXX world"));
+
+ /* fix "hello" */
+ assert_se(lseek(sd_memfd_get_fd(m), 2, SEEK_SET) == 2);
+ assert_se(write(sd_memfd_get_fd(m), "ll", 2) == 2);
+
+ assert_se(sd_memfd_get_file(m, &f) >= 0);
+ fputc('o', f);
+ fflush(f);
+
+ /* check content */
+ assert_se(streq(s, "hello world"));
+
+ assert_se(munmap(s, 12) == 0);
+
+ r = sd_memfd_get_sealed(m);
+ assert_se(r == 0);
+
+ r = sd_memfd_get_size(m, &sz);
+ assert_se(r >= 0);
+ assert_se(sz = page_size());
+
+ /* truncate it */
+ r = sd_memfd_set_size(m, 6);
+ assert_se(r >= 0);
+
+ /* get back new value */
+ r = sd_memfd_get_size(m, &sz);
+ assert_se(r >= 0);
+ assert_se(sz == 6);
+
+ r = sd_memfd_set_sealed(m, 1);
+ assert_se(r >= 0);
+
+ r = sd_memfd_get_sealed(m);
+ assert_se(r == 1);
+
+ fd = sd_memfd_dup_fd(m);
+ assert_se(fd >= 0);
+
+ sd_memfd_free(m);
+
+ /* new sd_memfd, same underlying memfd */
+ r = sd_memfd_make(fd, &m);
+ assert_se(r >= 0);
+
+ /* we did truncate it to 6 */
+ r = sd_memfd_get_size(m, &sz);
+ assert_se(r >= 0 && sz == 6);
+
+ /* map it, check content */
+ r = sd_memfd_map(m, 0, 12, (void **)&s);
+ assert_se(r >= 0);
+
+ /* we only see the truncated size */
+ assert_se(streq(s, "hello "));
+
+ /* it was already sealed */
+ r = sd_memfd_set_sealed(m, 1);
+ assert_se(r == -EALREADY);
+
+ /* we cannot break the seal, it is mapped */
+ r = sd_memfd_set_sealed(m, 0);
+ assert_se(r == -ETXTBSY);
+
+ /* unmap it; become the single owner */
+ assert_se(munmap(s, 12) == 0);
+
+ /* now we can do flip the sealing */
+ r = sd_memfd_set_sealed(m, 0);
+ assert_se(r == 0);
+ r = sd_memfd_get_sealed(m);
+ assert_se(r == 0);
+
+ r = sd_memfd_set_sealed(m, 1);
+ assert_se(r == 0);
+ r = sd_memfd_get_sealed(m);
+ assert_se(r == 1);
+
+ r = sd_memfd_set_sealed(m, 0);
+ assert_se(r == 0);
+ r = sd_memfd_get_sealed(m);
+ assert_se(r == 0);
+
+ /* seek at 2, read() 2 bytes */
+ assert_se(lseek(fd, 2, SEEK_SET) == 2);
+ assert_se(read(fd, buf, 2) == 2);
+
+ /* check content */
+ assert_se(memcmp(buf, "ll", 2) == 0);
+
+ /* writev it out*/
+ iov[0].iov_base = (char *)"ABC";
+ iov[0].iov_len = 3;
+ iov[1].iov_base = (char *)"DEF";
+ iov[1].iov_len = 3;
+ iov[2].iov_base = (char *)"GHI";
+ iov[2].iov_len = 3;
+ assert_se(pwritev(fd, iov, 3, 0) == 9);
+
+ /* readv it back */
+ iov[0].iov_base = bufv[0];
+ iov[0].iov_len = 3;
+ iov[1].iov_base = bufv[1];
+ iov[1].iov_len = 3;
+ iov[2].iov_base = bufv[2];
+ iov[2].iov_len = 3;
+ assert_se(preadv(fd, iov, 3, 0) == 9);
+
+ /* check content */
+ assert_se(memcmp(bufv[0], "ABC", 3) == 0);
+ assert_se(memcmp(bufv[1], "DEF", 3) == 0);
+ assert_se(memcmp(bufv[2], "GHI", 3) == 0);
+
+ sd_memfd_free(m);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c
new file mode 100644
index 0000000..e2423c7
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-objects.c
@@ -0,0 +1,500 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+#include "strv.h"
+
+#include "sd-bus.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-util.h"
+#include "bus-dump.h"
+
+struct context {
+ int fds[2];
+ bool quit;
+ char *something;
+ char *automatic_string_property;
+ uint32_t automatic_integer_property;
+};
+
+static int something_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ struct context *c = userdata;
+ const char *s;
+ char *n = NULL;
+ int r;
+
+ r = sd_bus_message_read(m, "s", &s);
+ assert_se(r > 0);
+
+ n = strjoin("<<<", s, ">>>", NULL);
+ assert_se(n);
+
+ free(c->something);
+ c->something = n;
+
+ log_info("AlterSomething() called, got %s, returning %s", s, n);
+
+ /* This should fail, since the return type doesn't match */
+ assert_se(sd_bus_reply_method_return(m, "u", 4711) == -ENOMSG);
+
+ r = sd_bus_reply_method_return(m, "s", n);
+ assert_se(r >= 0);
+
+ return 1;
+}
+
+static int exit_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ struct context *c = userdata;
+ int r;
+
+ c->quit = true;
+
+ log_info("Exit called");
+
+ r = sd_bus_reply_method_return(m, "");
+ assert_se(r >= 0);
+
+ return 1;
+}
+
+static int get_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+ struct context *c = userdata;
+ int r;
+
+ log_info("property get for %s called, returning \"%s\".", property, c->something);
+
+ r = sd_bus_message_append(reply, "s", c->something);
+ assert_se(r >= 0);
+
+ return 1;
+}
+
+static int set_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *error) {
+ struct context *c = userdata;
+ const char *s;
+ char *n;
+ int r;
+
+ log_info("property set for %s called", property);
+
+ r = sd_bus_message_read(value, "s", &s);
+ assert_se(r >= 0);
+
+ n = strdup(s);
+ assert_se(n);
+
+ free(c->something);
+ c->something = n;
+
+ return 1;
+}
+
+static int value_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char *s = NULL;
+ const char *x;
+ int r;
+
+ assert_se(asprintf(&s, "object %p, path %s", userdata, path) >= 0);
+ r = sd_bus_message_append(reply, "s", s);
+ assert_se(r >= 0);
+
+ assert_se(x = startswith(path, "/value/"));
+
+ assert_se(PTR_TO_UINT(userdata) == 30);
+
+ return 1;
+}
+
+static int notify_test(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ int r;
+
+ assert_se(sd_bus_emit_properties_changed(bus, m->path, "org.freedesktop.systemd.ValueTest", "Value", NULL) >= 0);
+
+ r = sd_bus_reply_method_return(m, NULL);
+ assert_se(r >= 0);
+
+ return 1;
+}
+
+static int notify_test2(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ int r;
+
+ assert_se(sd_bus_emit_properties_changed_strv(bus, m->path, "org.freedesktop.systemd.ValueTest", NULL) >= 0);
+
+ r = sd_bus_reply_method_return(m, NULL);
+ assert_se(r >= 0);
+
+ return 1;
+}
+
+static int emit_interfaces_added(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ int r;
+
+ assert_se(sd_bus_emit_interfaces_added(bus, m->path, "org.freedesktop.systemd.test", NULL) >= 0);
+
+ r = sd_bus_reply_method_return(m, NULL);
+ assert_se(r >= 0);
+
+ return 1;
+}
+
+static int emit_interfaces_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ int r;
+
+ assert_se(sd_bus_emit_interfaces_removed(bus, m->path, "org.freedesktop.systemd.test", NULL) >= 0);
+
+ r = sd_bus_reply_method_return(m, NULL);
+ assert_se(r >= 0);
+
+ return 1;
+}
+
+static const sd_bus_vtable vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ SD_BUS_METHOD("AlterSomething", "s", "s", something_handler, 0),
+ SD_BUS_METHOD("Exit", "", "", exit_handler, 0),
+ SD_BUS_WRITABLE_PROPERTY("Something", "s", get_handler, set_handler, 0, 0),
+ SD_BUS_WRITABLE_PROPERTY("AutomaticStringProperty", "s", NULL, NULL, offsetof(struct context, automatic_string_property), 0),
+ SD_BUS_WRITABLE_PROPERTY("AutomaticIntegerProperty", "u", NULL, NULL, offsetof(struct context, automatic_integer_property), 0),
+ SD_BUS_METHOD("NoOperation", NULL, NULL, NULL, 0),
+ SD_BUS_METHOD("EmitInterfacesAdded", NULL, NULL, emit_interfaces_added, 0),
+ SD_BUS_METHOD("EmitInterfacesRemoved", NULL, NULL, emit_interfaces_removed, 0),
+ SD_BUS_VTABLE_END
+};
+
+static const sd_bus_vtable vtable2[] = {
+ SD_BUS_VTABLE_START(0),
+ SD_BUS_METHOD("NotifyTest", "", "", notify_test, 0),
+ SD_BUS_METHOD("NotifyTest2", "", "", notify_test2, 0),
+ SD_BUS_PROPERTY("Value", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("Value2", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+ SD_BUS_PROPERTY("Value3", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("Value4", "s", value_handler, 10, 0),
+ SD_BUS_VTABLE_END
+};
+
+static int enumerator_callback(sd_bus *b, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+
+ if (object_path_startswith("/value", path))
+ assert_se(*nodes = strv_new("/value/a", "/value/b", "/value/c", NULL));
+
+ return 1;
+}
+
+static void *server(void *p) {
+ struct context *c = p;
+ sd_bus *bus = NULL;
+ sd_id128_t id;
+ int r;
+
+ c->quit = false;
+
+ assert_se(sd_id128_randomize(&id) >= 0);
+
+ assert_se(sd_bus_new(&bus) >= 0);
+ assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0);
+ assert_se(sd_bus_set_server(bus, 1, id) >= 0);
+
+ assert_se(sd_bus_add_object_vtable(bus, "/foo", "org.freedesktop.systemd.test", vtable, c) >= 0);
+ assert_se(sd_bus_add_object_vtable(bus, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0);
+ assert_se(sd_bus_add_fallback_vtable(bus, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0);
+ assert_se(sd_bus_add_node_enumerator(bus, "/value", enumerator_callback, NULL) >= 0);
+ assert_se(sd_bus_add_object_manager(bus, "/value") >= 0);
+
+ assert_se(sd_bus_start(bus) >= 0);
+
+ log_error("Entering event loop on server");
+
+ while (!c->quit) {
+ log_error("Loop!");
+
+ r = sd_bus_process(bus, NULL);
+ if (r < 0) {
+ log_error("Failed to process requests: %s", strerror(-r));
+ goto fail;
+ }
+
+ if (r == 0) {
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0) {
+ log_error("Failed to wait: %s", strerror(-r));
+ goto fail;
+ }
+
+ continue;
+ }
+ }
+
+ r = 0;
+
+fail:
+ if (bus) {
+ sd_bus_flush(bus);
+ sd_bus_unref(bus);
+ }
+
+ return INT_TO_PTR(r);
+}
+
+static int client(struct context *c) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ const char *s;
+ int r;
+
+ assert_se(sd_bus_new(&bus) >= 0);
+ assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0);
+ assert_se(sd_bus_start(bus) >= 0);
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "NoOperation", &error, NULL, NULL);
+ assert_se(r >= 0);
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "s", "hallo");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_read(reply, "s", &s);
+ assert_se(r >= 0);
+ assert_se(streq(s, "<<<hallo>>>"));
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Doesntexist", &error, &reply, "");
+ assert_se(r < 0);
+ assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD));
+
+ sd_bus_error_free(&error);
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "as", 1, "hallo");
+ assert_se(r < 0);
+ assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS));
+
+ sd_bus_error_free(&error);
+
+ r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_read(reply, "s", &s);
+ assert_se(r >= 0);
+ assert_se(streq(s, "<<<hallo>>>"));
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, "s", "test");
+ assert_se(r >= 0);
+
+ r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_read(reply, "s", &s);
+ assert_se(r >= 0);
+ assert_se(streq(s, "test"));
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticIntegerProperty", &error, "u", 815);
+ assert_se(r >= 0);
+
+ assert_se(c->automatic_integer_property == 815);
+
+ r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticStringProperty", &error, "s", "Du Dödel, Du!");
+ assert_se(r >= 0);
+
+ assert_se(streq(c->automatic_string_property, "Du Dödel, Du!"));
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_read(reply, "s", &s);
+ assert_se(r >= 0);
+ fputs(s, stdout);
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/value/xuzz", "org.freedesktop.systemd.ValueTest", "Value", &error, &reply, "s");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_read(reply, "s", &s);
+ assert_se(r >= 0);
+ log_info("read %s", s);
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_read(reply, "s", &s);
+ assert_se(r >= 0);
+ fputs(s, stdout);
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_read(reply, "s", &s);
+ assert_se(r >= 0);
+ fputs(s, stdout);
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_read(reply, "s", &s);
+ assert_se(r >= 0);
+ fputs(s, stdout);
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "");
+ assert_se(r >= 0);
+
+ bus_message_dump(reply, stdout, true);
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "org.freedesktop.systemd.ValueTest2");
+ assert_se(r < 0);
+ assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_INTERFACE));
+ sd_bus_error_free(&error);
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
+ assert_se(r < 0);
+ assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD));
+ sd_bus_error_free(&error);
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
+ assert_se(r >= 0);
+
+ bus_message_dump(reply, stdout, true);
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest", &error, NULL, "");
+ assert_se(r >= 0);
+
+ r = sd_bus_process(bus, &reply);
+ assert_se(r > 0);
+
+ assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
+ bus_message_dump(reply, stdout, true);
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest2", &error, NULL, "");
+ assert_se(r >= 0);
+
+ r = sd_bus_process(bus, &reply);
+ assert_se(r > 0);
+
+ assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
+ bus_message_dump(reply, stdout, true);
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesAdded", &error, NULL, "");
+ assert_se(r >= 0);
+
+ r = sd_bus_process(bus, &reply);
+ assert_se(r > 0);
+
+ assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
+ bus_message_dump(reply, stdout, true);
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesRemoved", &error, NULL, "");
+ assert_se(r >= 0);
+
+ r = sd_bus_process(bus, &reply);
+ assert_se(r > 0);
+
+ assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
+ bus_message_dump(reply, stdout, true);
+
+ sd_bus_message_unref(reply);
+ reply = NULL;
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, "");
+ assert_se(r >= 0);
+
+ sd_bus_flush(bus);
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ struct context c = {};
+ pthread_t s;
+ void *p;
+ int r, q;
+
+ zero(c);
+
+ c.automatic_integer_property = 4711;
+ assert_se(c.automatic_string_property = strdup("dudeldu"));
+
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0);
+
+ r = pthread_create(&s, NULL, server, &c);
+ if (r != 0)
+ return -r;
+
+ r = client(&c);
+
+ q = pthread_join(s, &p);
+ if (q != 0)
+ return -q;
+
+ if (r < 0)
+ return r;
+
+ if (PTR_TO_INT(p) < 0)
+ return PTR_TO_INT(p);
+
+ free(c.something);
+ free(c.automatic_string_property);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-server.c b/src/libsystemd/sd-bus/test-bus-server.c
new file mode 100644
index 0000000..0e65ee7
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-server.c
@@ -0,0 +1,224 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+
+#include "sd-bus.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-util.h"
+
+struct context {
+ int fds[2];
+
+ bool client_negotiate_unix_fds;
+ bool server_negotiate_unix_fds;
+
+ bool client_anonymous_auth;
+ bool server_anonymous_auth;
+};
+
+static void *server(void *p) {
+ struct context *c = p;
+ sd_bus *bus = NULL;
+ sd_id128_t id;
+ bool quit = false;
+ int r;
+
+ assert_se(sd_id128_randomize(&id) >= 0);
+
+ assert_se(sd_bus_new(&bus) >= 0);
+ assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0);
+ assert_se(sd_bus_set_server(bus, 1, id) >= 0);
+ assert_se(sd_bus_set_anonymous(bus, c->server_anonymous_auth) >= 0);
+ assert_se(sd_bus_negotiate_fds(bus, c->server_negotiate_unix_fds) >= 0);
+ assert_se(sd_bus_start(bus) >= 0);
+
+ while (!quit) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+
+ r = sd_bus_process(bus, &m);
+ if (r < 0) {
+ log_error("Failed to process requests: %s", strerror(-r));
+ goto fail;
+ }
+
+ if (r == 0) {
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0) {
+ log_error("Failed to wait: %s", strerror(-r));
+ goto fail;
+ }
+
+ continue;
+ }
+
+ if (!m)
+ continue;
+
+ log_info("Got message! member=%s", strna(sd_bus_message_get_member(m)));
+
+ if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Exit")) {
+
+ assert_se((sd_bus_can_send(bus, 'h') >= 1) == (c->server_negotiate_unix_fds && c->client_negotiate_unix_fds));
+
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0) {
+ log_error("Failed to allocate return: %s", strerror(-r));
+ goto fail;
+ }
+
+ quit = true;
+
+ } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
+ r = sd_bus_message_new_method_error(
+ m,
+ &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method."),
+ &reply);
+ if (r < 0) {
+ log_error("Failed to allocate return: %s", strerror(-r));
+ goto fail;
+ }
+ }
+
+ if (reply) {
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0) {
+ log_error("Failed to send reply: %s", strerror(-r));
+ goto fail;
+ }
+ }
+ }
+
+ r = 0;
+
+fail:
+ if (bus) {
+ sd_bus_flush(bus);
+ sd_bus_unref(bus);
+ }
+
+ return INT_TO_PTR(r);
+}
+
+static int client(struct context *c) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert_se(sd_bus_new(&bus) >= 0);
+ assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0);
+ assert_se(sd_bus_negotiate_fds(bus, c->client_negotiate_unix_fds) >= 0);
+ assert_se(sd_bus_set_anonymous(bus, c->client_anonymous_auth) >= 0);
+ assert_se(sd_bus_start(bus) >= 0);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ "org.freedesktop.systemd.test",
+ "/",
+ "org.freedesktop.systemd.test",
+ "Exit",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate method call: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
+ return r;
+ }
+
+ return 0;
+}
+
+static int test_one(bool client_negotiate_unix_fds, bool server_negotiate_unix_fds,
+ bool client_anonymous_auth, bool server_anonymous_auth) {
+
+ struct context c;
+ pthread_t s;
+ void *p;
+ int r, q;
+
+ zero(c);
+
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0);
+
+ c.client_negotiate_unix_fds = client_negotiate_unix_fds;
+ c.server_negotiate_unix_fds = server_negotiate_unix_fds;
+ c.client_anonymous_auth = client_anonymous_auth;
+ c.server_anonymous_auth = server_anonymous_auth;
+
+ r = pthread_create(&s, NULL, server, &c);
+ if (r != 0)
+ return -r;
+
+ r = client(&c);
+
+ q = pthread_join(s, &p);
+ if (q != 0)
+ return -q;
+
+ if (r < 0)
+ return r;
+
+ if (PTR_TO_INT(p) < 0)
+ return PTR_TO_INT(p);
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+
+ r = test_one(true, true, false, false);
+ assert_se(r >= 0);
+
+ r = test_one(true, false, false, false);
+ assert_se(r >= 0);
+
+ r = test_one(false, true, false, false);
+ assert_se(r >= 0);
+
+ r = test_one(false, false, false, false);
+ assert_se(r >= 0);
+
+ r = test_one(true, true, true, true);
+ assert_se(r >= 0);
+
+ r = test_one(true, true, false, true);
+ assert_se(r >= 0);
+
+ r = test_one(true, true, true, false);
+ assert_se(r == -EPERM);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-signature.c b/src/libsystemd/sd-bus/test-bus-signature.c
new file mode 100644
index 0000000..3fc565c
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-signature.c
@@ -0,0 +1,163 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "log.h"
+#include "bus-signature.h"
+#include "bus-internal.h"
+
+int main(int argc, char *argv[]) {
+ char prefix[256];
+ int r;
+
+ assert_se(signature_is_single("y", false));
+ assert_se(signature_is_single("u", false));
+ assert_se(signature_is_single("v", false));
+ assert_se(signature_is_single("as", false));
+ assert_se(signature_is_single("(ss)", false));
+ assert_se(signature_is_single("()", false));
+ assert_se(signature_is_single("(()()()()())", false));
+ assert_se(signature_is_single("(((())))", false));
+ assert_se(signature_is_single("((((s))))", false));
+ assert_se(signature_is_single("{ss}", true));
+ assert_se(signature_is_single("a{ss}", false));
+ assert_se(!signature_is_single("uu", false));
+ assert_se(!signature_is_single("", false));
+ assert_se(!signature_is_single("(", false));
+ assert_se(!signature_is_single(")", false));
+ assert_se(!signature_is_single("())", false));
+ assert_se(!signature_is_single("((())", false));
+ assert_se(!signature_is_single("{)", false));
+ assert_se(!signature_is_single("{}", true));
+ assert_se(!signature_is_single("{sss}", true));
+ assert_se(!signature_is_single("{s}", true));
+ assert_se(!signature_is_single("{ss}", false));
+ assert_se(!signature_is_single("{ass}", true));
+ assert_se(!signature_is_single("a}", true));
+
+ assert_se(signature_is_pair("yy"));
+ assert_se(signature_is_pair("ss"));
+ assert_se(signature_is_pair("sas"));
+ assert_se(signature_is_pair("sv"));
+ assert_se(signature_is_pair("sa(vs)"));
+ assert_se(!signature_is_pair(""));
+ assert_se(!signature_is_pair("va"));
+ assert_se(!signature_is_pair("sss"));
+ assert_se(!signature_is_pair("{s}ss"));
+
+ assert_se(signature_is_valid("ssa{ss}sssub", true));
+ assert_se(signature_is_valid("ssa{ss}sssub", false));
+ assert_se(signature_is_valid("{ss}", true));
+ assert_se(!signature_is_valid("{ss}", false));
+ assert_se(signature_is_valid("", true));
+ assert_se(signature_is_valid("", false));
+
+ assert_se(signature_is_valid("sssusa(uuubbba(uu)uuuu)a{u(uuuvas)}", false));
+
+ assert_se(!signature_is_valid("a", false));
+ assert_se(signature_is_valid("as", false));
+ assert_se(signature_is_valid("aas", false));
+ assert_se(signature_is_valid("aaas", false));
+ assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad", false));
+ assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaas", false));
+ assert_se(!signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaau", false));
+
+ assert_se(signature_is_valid("(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))", false));
+ assert_se(!signature_is_valid("((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))", false));
+
+ assert_se(namespace_complex_pattern("", ""));
+ assert_se(namespace_complex_pattern("foobar", "foobar"));
+ assert_se(namespace_complex_pattern("foobar.waldo", "foobar.waldo"));
+ assert_se(namespace_complex_pattern("foobar.", "foobar.waldo"));
+ assert_se(namespace_complex_pattern("foobar.waldo", "foobar."));
+ assert_se(!namespace_complex_pattern("foobar.waldo", "foobar"));
+ assert_se(!namespace_complex_pattern("foobar", "foobar.waldo"));
+ assert_se(!namespace_complex_pattern("", "foo"));
+ assert_se(!namespace_complex_pattern("foo", ""));
+ assert_se(!namespace_complex_pattern("foo.", ""));
+
+ assert_se(path_complex_pattern("", ""));
+ assert_se(path_complex_pattern("", "/"));
+ assert_se(path_complex_pattern("/", ""));
+ assert_se(path_complex_pattern("/", "/"));
+ assert_se(path_complex_pattern("/foobar/", "/"));
+ assert_se(path_complex_pattern("/foobar/", "/foobar"));
+ assert_se(path_complex_pattern("/foobar", "/foobar"));
+ assert_se(path_complex_pattern("/foobar", "/foobar/"));
+ assert_se(!path_complex_pattern("/foobar", "/foobar/waldo"));
+ assert_se(path_complex_pattern("/foobar/", "/foobar/waldo"));
+
+ assert_se(namespace_simple_pattern("", ""));
+ assert_se(namespace_simple_pattern("foobar", "foobar"));
+ assert_se(namespace_simple_pattern("foobar.waldo", "foobar.waldo"));
+ assert_se(namespace_simple_pattern("foobar", "foobar.waldo"));
+ assert_se(!namespace_simple_pattern("foobar.waldo", "foobar"));
+ assert_se(!namespace_simple_pattern("", "foo"));
+ assert_se(!namespace_simple_pattern("foo", ""));
+
+ assert_se(streq(object_path_startswith("/foo/bar", "/foo"), "bar"));
+ assert_se(streq(object_path_startswith("/foo", "/foo"), ""));
+ assert_se(streq(object_path_startswith("/foo", "/"), "foo"));
+ assert_se(streq(object_path_startswith("/", "/"), ""));
+ assert_se(!object_path_startswith("/foo", "/bar"));
+ assert_se(!object_path_startswith("/", "/bar"));
+ assert_se(!object_path_startswith("/foo", ""));
+
+ assert_se(object_path_is_valid("/foo/bar"));
+ assert_se(object_path_is_valid("/foo"));
+ assert_se(object_path_is_valid("/"));
+ assert_se(object_path_is_valid("/foo5"));
+ assert_se(object_path_is_valid("/foo_5"));
+ assert_se(!object_path_is_valid(""));
+ assert_se(!object_path_is_valid("/foo/"));
+ assert_se(!object_path_is_valid("//"));
+ assert_se(!object_path_is_valid("//foo"));
+ assert_se(!object_path_is_valid("/foo//bar"));
+ assert_se(!object_path_is_valid("/foo/aaaäöä"));
+
+ OBJECT_PATH_FOREACH_PREFIX(prefix, "/") {
+ log_info("<%s>", prefix);
+ assert_not_reached("???");
+ }
+
+ r = 0;
+ OBJECT_PATH_FOREACH_PREFIX(prefix, "/xxx") {
+ log_info("<%s>", prefix);
+ assert_se(streq(prefix, "/"));
+ assert_se(r == 0);
+ r++;
+ }
+ assert_se(r == 1);
+
+ r = 0;
+ OBJECT_PATH_FOREACH_PREFIX(prefix, "/xxx/yyy/zzz") {
+ log_info("<%s>", prefix);
+ assert_se(r != 0 || streq(prefix, "/xxx/yyy"));
+ assert_se(r != 1 || streq(prefix, "/xxx"));
+ assert_se(r != 2 || streq(prefix, "/"));
+ r++;
+ }
+ assert_se(r == 3);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-zero-copy.c b/src/libsystemd/sd-bus/test-bus-zero-copy.c
new file mode 100644
index 0000000..5ad8ed5
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-zero-copy.c
@@ -0,0 +1,197 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "util.h"
+#include "log.h"
+
+#include "sd-bus.h"
+#include "sd-memfd.h"
+#include "bus-message.h"
+#include "bus-error.h"
+#include "bus-kernel.h"
+#include "bus-dump.h"
+
+#define FIRST_ARRAY 17
+#define SECOND_ARRAY 33
+
+#define STRING_SIZE 123
+
+int main(int argc, char *argv[]) {
+ _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL;
+ uint8_t *p;
+ sd_bus *a, *b;
+ int r, bus_ref;
+ sd_bus_message *m;
+ sd_memfd *f;
+ uint64_t sz;
+ uint32_t u32;
+ size_t i, l;
+ char *s;
+
+ log_set_max_level(LOG_DEBUG);
+
+ assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
+
+ bus_ref = bus_kernel_create_bus(name, false, &bus_name);
+ if (bus_ref == -ENOENT)
+ return EXIT_TEST_SKIP;
+
+ assert_se(bus_ref >= 0);
+
+ address = strappend("kernel:path=", bus_name);
+ assert_se(address);
+
+ r = sd_bus_new(&a);
+ assert_se(r >= 0);
+
+ r = sd_bus_new(&b);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_address(a, address);
+ assert_se(r >= 0);
+
+ r = sd_bus_set_address(b, address);
+ assert_se(r >= 0);
+
+ r = sd_bus_start(a);
+ assert_se(r >= 0);
+
+ r = sd_bus_start(b);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_new_method_call(b, ":1.1", "/a/path", "an.inter.face", "AMethod", &m);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_open_container(m, 'r', "aysay");
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append_array_space(m, 'y', FIRST_ARRAY, (void**) &p);
+ assert_se(r >= 0);
+
+ p[0] = '<';
+ memset(p+1, 'L', FIRST_ARRAY-2);
+ p[FIRST_ARRAY-1] = '>';
+
+ r = sd_memfd_new_and_map(&f, STRING_SIZE, (void**) &s);
+ assert_se(r >= 0);
+
+ s[0] = '<';
+ for (i = 1; i < STRING_SIZE-2; i++)
+ s[i] = '0' + (i % 10);
+ s[STRING_SIZE-2] = '>';
+ s[STRING_SIZE-1] = 0;
+ munmap(s, STRING_SIZE);
+
+ r = sd_memfd_get_size(f, &sz);
+ assert_se(r >= 0);
+ assert_se(sz == STRING_SIZE);
+
+ r = sd_bus_message_append_string_memfd(m, f);
+ assert_se(r >= 0);
+
+ sd_memfd_free(f);
+
+ r = sd_memfd_new_and_map(&f, SECOND_ARRAY, (void**) &p);
+ assert_se(r >= 0);
+
+ p[0] = '<';
+ memset(p+1, 'P', SECOND_ARRAY-2);
+ p[SECOND_ARRAY-1] = '>';
+ munmap(p, SECOND_ARRAY);
+
+ r = sd_memfd_get_size(f, &sz);
+ assert_se(r >= 0);
+ assert_se(sz == SECOND_ARRAY);
+
+ r = sd_bus_message_append_array_memfd(m, 'y', f);
+ assert_se(r >= 0);
+
+ sd_memfd_free(f);
+
+ r = sd_bus_message_close_container(m);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_append(m, "u", 4711);
+ assert_se(r >= 0);
+
+ r = bus_message_seal(m, 55, 99*USEC_PER_SEC);
+ assert_se(r >= 0);
+
+ bus_message_dump(m, stdout, true);
+
+ r = sd_bus_send(b, m, NULL);
+ assert_se(r >= 0);
+
+ sd_bus_message_unref(m);
+
+ r = sd_bus_process(a, &m);
+ assert_se(r > 0);
+
+ bus_message_dump(m, stdout, true);
+ sd_bus_message_rewind(m, true);
+
+ r = sd_bus_message_enter_container(m, 'r', "aysay");
+ assert_se(r > 0);
+
+ r = sd_bus_message_read_array(m, 'y', (const void**) &p, &l);
+ assert_se(r > 0);
+ assert_se(l == FIRST_ARRAY);
+
+ assert_se(p[0] == '<');
+ for (i = 1; i < l-1; i++)
+ assert_se(p[i] == 'L');
+ assert_se(p[l-1] == '>');
+
+ r = sd_bus_message_read(m, "s", &s);
+ assert_se(r > 0);
+
+ assert_se(s[0] == '<');
+ for (i = 1; i < STRING_SIZE-2; i++)
+ assert_se(s[i] == (char) ('0' + (i % 10)));
+ assert_se(s[STRING_SIZE-2] == '>');
+ assert_se(s[STRING_SIZE-1] == 0);
+
+ r = sd_bus_message_read_array(m, 'y', (const void**) &p, &l);
+ assert_se(r > 0);
+ assert_se(l == SECOND_ARRAY);
+
+ assert_se(p[0] == '<');
+ for (i = 1; i < l-1; i++)
+ assert_se(p[i] == 'P');
+ assert_se(p[l-1] == '>');
+
+ r = sd_bus_message_exit_container(m);
+ assert_se(r > 0);
+
+ r = sd_bus_message_read(m, "u", &u32);
+ assert_se(r > 0);
+ assert_se(u32 == 4711);
+
+ sd_bus_message_unref(m);
+
+ sd_bus_unref(a);
+ sd_bus_unref(b);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-event.c b/src/libsystemd/sd-event.c
deleted file mode 100644
index f33a9ec..0000000
--- a/src/libsystemd/sd-event.c
+++ /dev/null
@@ -1,2224 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <sys/wait.h>
-#include <pthread.h>
-
-#include "sd-id128.h"
-#include "sd-daemon.h"
-#include "macro.h"
-#include "prioq.h"
-#include "hashmap.h"
-#include "util.h"
-#include "time-util.h"
-#include "missing.h"
-
-#include "sd-event.h"
-
-#define EPOLL_QUEUE_MAX 512U
-#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
-
-typedef enum EventSourceType {
- SOURCE_IO,
- SOURCE_MONOTONIC,
- SOURCE_REALTIME,
- SOURCE_SIGNAL,
- SOURCE_CHILD,
- SOURCE_DEFER,
- SOURCE_EXIT,
- SOURCE_WATCHDOG
-} EventSourceType;
-
-struct sd_event_source {
- unsigned n_ref;
-
- sd_event *event;
- void *userdata;
- sd_event_handler_t prepare;
-
- EventSourceType type:4;
- int enabled:3;
- bool pending:1;
- bool dispatching:1;
-
- int64_t priority;
- unsigned pending_index;
- unsigned prepare_index;
- unsigned pending_iteration;
- unsigned prepare_iteration;
-
- union {
- struct {
- sd_event_io_handler_t callback;
- int fd;
- uint32_t events;
- uint32_t revents;
- bool registered:1;
- } io;
- struct {
- sd_event_time_handler_t callback;
- usec_t next, accuracy;
- unsigned earliest_index;
- unsigned latest_index;
- } time;
- struct {
- sd_event_signal_handler_t callback;
- struct signalfd_siginfo siginfo;
- int sig;
- } signal;
- struct {
- sd_event_child_handler_t callback;
- siginfo_t siginfo;
- pid_t pid;
- int options;
- } child;
- struct {
- sd_event_handler_t callback;
- } defer;
- struct {
- sd_event_handler_t callback;
- unsigned prioq_index;
- } exit;
- };
-};
-
-struct sd_event {
- unsigned n_ref;
-
- int epoll_fd;
- int signal_fd;
- int realtime_fd;
- int monotonic_fd;
- int watchdog_fd;
-
- Prioq *pending;
- Prioq *prepare;
-
- /* For both clocks we maintain two priority queues each, one
- * ordered for the earliest times the events may be
- * dispatched, and one ordered by the latest times they must
- * have been dispatched. The range between the top entries in
- * the two prioqs is the time window we can freely schedule
- * wakeups in */
- Prioq *monotonic_earliest;
- Prioq *monotonic_latest;
- Prioq *realtime_earliest;
- Prioq *realtime_latest;
-
- usec_t realtime_next, monotonic_next;
- usec_t perturb;
-
- sigset_t sigset;
- sd_event_source **signal_sources;
-
- Hashmap *child_sources;
- unsigned n_enabled_child_sources;
-
- Prioq *exit;
-
- pid_t original_pid;
-
- unsigned iteration;
- dual_timestamp timestamp;
- int state;
-
- bool exit_requested:1;
- bool need_process_child:1;
- bool watchdog:1;
-
- int exit_code;
-
- pid_t tid;
- sd_event **default_event_ptr;
-
- usec_t watchdog_last, watchdog_period;
-
- unsigned n_sources;
-};
-
-static int pending_prioq_compare(const void *a, const void *b) {
- const sd_event_source *x = a, *y = b;
-
- assert(x->pending);
- assert(y->pending);
-
- /* Enabled ones first */
- if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
- return -1;
- if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
- return 1;
-
- /* Lower priority values first */
- if (x->priority < y->priority)
- return -1;
- if (x->priority > y->priority)
- return 1;
-
- /* Older entries first */
- if (x->pending_iteration < y->pending_iteration)
- return -1;
- if (x->pending_iteration > y->pending_iteration)
- return 1;
-
- /* Stability for the rest */
- if (x < y)
- return -1;
- if (x > y)
- return 1;
-
- return 0;
-}
-
-static int prepare_prioq_compare(const void *a, const void *b) {
- const sd_event_source *x = a, *y = b;
-
- assert(x->prepare);
- assert(y->prepare);
-
- /* Move most recently prepared ones last, so that we can stop
- * preparing as soon as we hit one that has already been
- * prepared in the current iteration */
- if (x->prepare_iteration < y->prepare_iteration)
- return -1;
- if (x->prepare_iteration > y->prepare_iteration)
- return 1;
-
- /* Enabled ones first */
- if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
- return -1;
- if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
- return 1;
-
- /* Lower priority values first */
- if (x->priority < y->priority)
- return -1;
- if (x->priority > y->priority)
- return 1;
-
- /* Stability for the rest */
- if (x < y)
- return -1;
- if (x > y)
- return 1;
-
- return 0;
-}
-
-static int earliest_time_prioq_compare(const void *a, const void *b) {
- const sd_event_source *x = a, *y = b;
-
- assert(x->type == SOURCE_MONOTONIC || x->type == SOURCE_REALTIME);
- assert(y->type == SOURCE_MONOTONIC || y->type == SOURCE_REALTIME);
-
- /* Enabled ones first */
- if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
- return -1;
- if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
- return 1;
-
- /* Move the pending ones to the end */
- if (!x->pending && y->pending)
- return -1;
- if (x->pending && !y->pending)
- return 1;
-
- /* Order by time */
- if (x->time.next < y->time.next)
- return -1;
- if (x->time.next > y->time.next)
- return 1;
-
- /* Stability for the rest */
- if (x < y)
- return -1;
- if (x > y)
- return 1;
-
- return 0;
-}
-
-static int latest_time_prioq_compare(const void *a, const void *b) {
- const sd_event_source *x = a, *y = b;
-
- assert((x->type == SOURCE_MONOTONIC && y->type == SOURCE_MONOTONIC) ||
- (x->type == SOURCE_REALTIME && y->type == SOURCE_REALTIME));
-
- /* Enabled ones first */
- if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
- return -1;
- if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
- return 1;
-
- /* Move the pending ones to the end */
- if (!x->pending && y->pending)
- return -1;
- if (x->pending && !y->pending)
- return 1;
-
- /* Order by time */
- if (x->time.next + x->time.accuracy < y->time.next + y->time.accuracy)
- return -1;
- if (x->time.next + x->time.accuracy > y->time.next + y->time.accuracy)
- return 1;
-
- /* Stability for the rest */
- if (x < y)
- return -1;
- if (x > y)
- return 1;
-
- return 0;
-}
-
-static int exit_prioq_compare(const void *a, const void *b) {
- const sd_event_source *x = a, *y = b;
-
- assert(x->type == SOURCE_EXIT);
- assert(y->type == SOURCE_EXIT);
-
- /* Enabled ones first */
- if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
- return -1;
- if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
- return 1;
-
- /* Lower priority values first */
- if (x->priority < y->priority)
- return -1;
- if (x->priority > y->priority)
- return 1;
-
- /* Stability for the rest */
- if (x < y)
- return -1;
- if (x > y)
- return 1;
-
- return 0;
-}
-
-static void event_free(sd_event *e) {
- assert(e);
- assert(e->n_sources == 0);
-
- if (e->default_event_ptr)
- *(e->default_event_ptr) = NULL;
-
- if (e->epoll_fd >= 0)
- close_nointr_nofail(e->epoll_fd);
-
- if (e->signal_fd >= 0)
- close_nointr_nofail(e->signal_fd);
-
- if (e->realtime_fd >= 0)
- close_nointr_nofail(e->realtime_fd);
-
- if (e->monotonic_fd >= 0)
- close_nointr_nofail(e->monotonic_fd);
-
- if (e->watchdog_fd >= 0)
- close_nointr_nofail(e->watchdog_fd);
-
- prioq_free(e->pending);
- prioq_free(e->prepare);
- prioq_free(e->monotonic_earliest);
- prioq_free(e->monotonic_latest);
- prioq_free(e->realtime_earliest);
- prioq_free(e->realtime_latest);
- prioq_free(e->exit);
-
- free(e->signal_sources);
-
- hashmap_free(e->child_sources);
- free(e);
-}
-
-_public_ int sd_event_new(sd_event** ret) {
- sd_event *e;
- int r;
-
- assert_return(ret, -EINVAL);
-
- e = new0(sd_event, 1);
- if (!e)
- return -ENOMEM;
-
- e->n_ref = 1;
- e->signal_fd = e->realtime_fd = e->monotonic_fd = e->watchdog_fd = e->epoll_fd = -1;
- e->realtime_next = e->monotonic_next = (usec_t) -1;
- e->original_pid = getpid();
-
- assert_se(sigemptyset(&e->sigset) == 0);
-
- e->pending = prioq_new(pending_prioq_compare);
- if (!e->pending) {
- r = -ENOMEM;
- goto fail;
- }
-
- e->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
- if (e->epoll_fd < 0) {
- r = -errno;
- goto fail;
- }
-
- *ret = e;
- return 0;
-
-fail:
- event_free(e);
- return r;
-}
-
-_public_ sd_event* sd_event_ref(sd_event *e) {
- assert_return(e, NULL);
-
- assert(e->n_ref >= 1);
- e->n_ref++;
-
- return e;
-}
-
-_public_ sd_event* sd_event_unref(sd_event *e) {
-
- if (!e)
- return NULL;
-
- assert(e->n_ref >= 1);
- e->n_ref--;
-
- if (e->n_ref <= 0)
- event_free(e);
-
- return NULL;
-}
-
-static bool event_pid_changed(sd_event *e) {
- assert(e);
-
- /* We don't support people creating am event loop and keeping
- * it around over a fork(). Let's complain. */
-
- return e->original_pid != getpid();
-}
-
-static int source_io_unregister(sd_event_source *s) {
- int r;
-
- assert(s);
- assert(s->type == SOURCE_IO);
-
- if (!s->io.registered)
- return 0;
-
- r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL);
- if (r < 0)
- return -errno;
-
- s->io.registered = false;
- return 0;
-}
-
-static int source_io_register(
- sd_event_source *s,
- int enabled,
- uint32_t events) {
-
- struct epoll_event ev = {};
- int r;
-
- assert(s);
- assert(s->type == SOURCE_IO);
- assert(enabled != SD_EVENT_OFF);
-
- ev.events = events;
- ev.data.ptr = s;
-
- if (enabled == SD_EVENT_ONESHOT)
- ev.events |= EPOLLONESHOT;
-
- if (s->io.registered)
- r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_MOD, s->io.fd, &ev);
- else
- r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_ADD, s->io.fd, &ev);
-
- if (r < 0)
- return -errno;
-
- s->io.registered = true;
-
- return 0;
-}
-
-static void source_free(sd_event_source *s) {
- assert(s);
-
- if (s->event) {
- assert(s->event->n_sources > 0);
-
- switch (s->type) {
-
- case SOURCE_IO:
- if (s->io.fd >= 0)
- source_io_unregister(s);
-
- break;
-
- case SOURCE_MONOTONIC:
- prioq_remove(s->event->monotonic_earliest, s, &s->time.earliest_index);
- prioq_remove(s->event->monotonic_latest, s, &s->time.latest_index);
- break;
-
- case SOURCE_REALTIME:
- prioq_remove(s->event->realtime_earliest, s, &s->time.earliest_index);
- prioq_remove(s->event->realtime_latest, s, &s->time.latest_index);
- break;
-
- case SOURCE_SIGNAL:
- if (s->signal.sig > 0) {
- if (s->signal.sig != SIGCHLD || s->event->n_enabled_child_sources == 0)
- assert_se(sigdelset(&s->event->sigset, s->signal.sig) == 0);
-
- if (s->event->signal_sources)
- s->event->signal_sources[s->signal.sig] = NULL;
- }
-
- break;
-
- case SOURCE_CHILD:
- if (s->child.pid > 0) {
- if (s->enabled != SD_EVENT_OFF) {
- assert(s->event->n_enabled_child_sources > 0);
- s->event->n_enabled_child_sources--;
- }
-
- if (!s->event->signal_sources || !s->event->signal_sources[SIGCHLD])
- assert_se(sigdelset(&s->event->sigset, SIGCHLD) == 0);
-
- hashmap_remove(s->event->child_sources, INT_TO_PTR(s->child.pid));
- }
-
- break;
-
- case SOURCE_DEFER:
- /* nothing */
- break;
-
- case SOURCE_EXIT:
- prioq_remove(s->event->exit, s, &s->exit.prioq_index);
- break;
-
- case SOURCE_WATCHDOG:
- assert_not_reached("Wut? I shouldn't exist.");
- }
-
- if (s->pending)
- prioq_remove(s->event->pending, s, &s->pending_index);
-
- if (s->prepare)
- prioq_remove(s->event->prepare, s, &s->prepare_index);
-
- s->event->n_sources--;
- sd_event_unref(s->event);
- }
-
- free(s);
-}
-
-static int source_set_pending(sd_event_source *s, bool b) {
- int r;
-
- assert(s);
- assert(s->type != SOURCE_EXIT);
-
- if (s->pending == b)
- return 0;
-
- s->pending = b;
-
- if (b) {
- s->pending_iteration = s->event->iteration;
-
- r = prioq_put(s->event->pending, s, &s->pending_index);
- if (r < 0) {
- s->pending = false;
- return r;
- }
- } else
- assert_se(prioq_remove(s->event->pending, s, &s->pending_index));
-
- if (s->type == SOURCE_REALTIME) {
- prioq_reshuffle(s->event->realtime_earliest, s, &s->time.earliest_index);
- prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index);
- } else if (s->type == SOURCE_MONOTONIC) {
- prioq_reshuffle(s->event->monotonic_earliest, s, &s->time.earliest_index);
- prioq_reshuffle(s->event->monotonic_latest, s, &s->time.latest_index);
- }
-
- return 0;
-}
-
-static sd_event_source *source_new(sd_event *e, EventSourceType type) {
- sd_event_source *s;
-
- assert(e);
-
- s = new0(sd_event_source, 1);
- if (!s)
- return NULL;
-
- s->n_ref = 1;
- s->event = sd_event_ref(e);
- s->type = type;
- s->pending_index = s->prepare_index = PRIOQ_IDX_NULL;
-
- e->n_sources ++;
-
- return s;
-}
-
-_public_ int sd_event_add_io(
- sd_event *e,
- int fd,
- uint32_t events,
- sd_event_io_handler_t callback,
- void *userdata,
- sd_event_source **ret) {
-
- sd_event_source *s;
- int r;
-
- assert_return(e, -EINVAL);
- assert_return(fd >= 0, -EINVAL);
- assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- s = source_new(e, SOURCE_IO);
- if (!s)
- return -ENOMEM;
-
- s->io.fd = fd;
- s->io.events = events;
- s->io.callback = callback;
- s->userdata = userdata;
- s->enabled = SD_EVENT_ON;
-
- r = source_io_register(s, s->enabled, events);
- if (r < 0) {
- source_free(s);
- return -errno;
- }
-
- *ret = s;
- return 0;
-}
-
-static int event_setup_timer_fd(
- sd_event *e,
- EventSourceType type,
- int *timer_fd,
- clockid_t id) {
-
- struct epoll_event ev = {};
- int r, fd;
- sd_id128_t bootid;
-
- assert(e);
- assert(timer_fd);
-
- if (_likely_(*timer_fd >= 0))
- return 0;
-
- fd = timerfd_create(id, TFD_NONBLOCK|TFD_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- ev.events = EPOLLIN;
- ev.data.ptr = INT_TO_PTR(type);
-
- r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, fd, &ev);
- if (r < 0) {
- close_nointr_nofail(fd);
- return -errno;
- }
-
- /* When we sleep for longer, we try to realign the wakeup to
- the same time wihtin each minute/second/250ms, so that
- events all across the system can be coalesced into a single
- CPU wakeup. However, let's take some system-specific
- randomness for this value, so that in a network of systems
- with synced clocks timer events are distributed a
- bit. Here, we calculate a perturbation usec offset from the
- boot ID. */
-
- if (sd_id128_get_boot(&bootid) >= 0)
- e->perturb = (bootid.qwords[0] ^ bootid.qwords[1]) % USEC_PER_MINUTE;
-
- *timer_fd = fd;
- return 0;
-}
-
-static int event_add_time_internal(
- sd_event *e,
- EventSourceType type,
- int *timer_fd,
- clockid_t id,
- Prioq **earliest,
- Prioq **latest,
- uint64_t usec,
- uint64_t accuracy,
- sd_event_time_handler_t callback,
- void *userdata,
- sd_event_source **ret) {
-
- sd_event_source *s;
- int r;
-
- assert_return(e, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(usec != (uint64_t) -1, -EINVAL);
- assert_return(accuracy != (uint64_t) -1, -EINVAL);
- assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- assert(timer_fd);
- assert(earliest);
- assert(latest);
-
- if (!*earliest) {
- *earliest = prioq_new(earliest_time_prioq_compare);
- if (!*earliest)
- return -ENOMEM;
- }
-
- if (!*latest) {
- *latest = prioq_new(latest_time_prioq_compare);
- if (!*latest)
- return -ENOMEM;
- }
-
- if (*timer_fd < 0) {
- r = event_setup_timer_fd(e, type, timer_fd, id);
- if (r < 0)
- return r;
- }
-
- s = source_new(e, type);
- if (!s)
- return -ENOMEM;
-
- s->time.next = usec;
- s->time.accuracy = accuracy == 0 ? DEFAULT_ACCURACY_USEC : accuracy;
- s->time.callback = callback;
- s->time.earliest_index = s->time.latest_index = PRIOQ_IDX_NULL;
- s->userdata = userdata;
- s->enabled = SD_EVENT_ONESHOT;
-
- r = prioq_put(*earliest, s, &s->time.earliest_index);
- if (r < 0)
- goto fail;
-
- r = prioq_put(*latest, s, &s->time.latest_index);
- if (r < 0)
- goto fail;
-
- *ret = s;
- return 0;
-
-fail:
- source_free(s);
- return r;
-}
-
-_public_ int sd_event_add_monotonic(sd_event *e,
- uint64_t usec,
- uint64_t accuracy,
- sd_event_time_handler_t callback,
- void *userdata,
- sd_event_source **ret) {
-
- return event_add_time_internal(e, SOURCE_MONOTONIC, &e->monotonic_fd, CLOCK_MONOTONIC, &e->monotonic_earliest, &e->monotonic_latest, usec, accuracy, callback, userdata, ret);
-}
-
-_public_ int sd_event_add_realtime(sd_event *e,
- uint64_t usec,
- uint64_t accuracy,
- sd_event_time_handler_t callback,
- void *userdata,
- sd_event_source **ret) {
-
- return event_add_time_internal(e, SOURCE_REALTIME, &e->realtime_fd, CLOCK_REALTIME, &e->realtime_earliest, &e->monotonic_latest, usec, accuracy, callback, userdata, ret);
-}
-
-static int event_update_signal_fd(sd_event *e) {
- struct epoll_event ev = {};
- bool add_to_epoll;
- int r;
-
- assert(e);
-
- add_to_epoll = e->signal_fd < 0;
-
- r = signalfd(e->signal_fd, &e->sigset, SFD_NONBLOCK|SFD_CLOEXEC);
- if (r < 0)
- return -errno;
-
- e->signal_fd = r;
-
- if (!add_to_epoll)
- return 0;
-
- ev.events = EPOLLIN;
- ev.data.ptr = INT_TO_PTR(SOURCE_SIGNAL);
-
- r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->signal_fd, &ev);
- if (r < 0) {
- close_nointr_nofail(e->signal_fd);
- e->signal_fd = -1;
-
- return -errno;
- }
-
- return 0;
-}
-
-_public_ int sd_event_add_signal(
- sd_event *e,
- int sig,
- sd_event_signal_handler_t callback,
- void *userdata,
- sd_event_source **ret) {
-
- sd_event_source *s;
- sigset_t ss;
- int r;
-
- assert_return(e, -EINVAL);
- assert_return(sig > 0, -EINVAL);
- assert_return(sig < _NSIG, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- r = pthread_sigmask(SIG_SETMASK, NULL, &ss);
- if (r < 0)
- return -errno;
-
- if (!sigismember(&ss, sig))
- return -EBUSY;
-
- if (!e->signal_sources) {
- e->signal_sources = new0(sd_event_source*, _NSIG);
- if (!e->signal_sources)
- return -ENOMEM;
- } else if (e->signal_sources[sig])
- return -EBUSY;
-
- s = source_new(e, SOURCE_SIGNAL);
- if (!s)
- return -ENOMEM;
-
- s->signal.sig = sig;
- s->signal.callback = callback;
- s->userdata = userdata;
- s->enabled = SD_EVENT_ON;
-
- e->signal_sources[sig] = s;
- assert_se(sigaddset(&e->sigset, sig) == 0);
-
- if (sig != SIGCHLD || e->n_enabled_child_sources == 0) {
- r = event_update_signal_fd(e);
- if (r < 0) {
- source_free(s);
- return r;
- }
- }
-
- *ret = s;
- return 0;
-}
-
-_public_ int sd_event_add_child(
- sd_event *e,
- pid_t pid,
- int options,
- sd_event_child_handler_t callback,
- void *userdata,
- sd_event_source **ret) {
-
- sd_event_source *s;
- int r;
-
- assert_return(e, -EINVAL);
- assert_return(pid > 1, -EINVAL);
- assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL);
- assert_return(options != 0, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- r = hashmap_ensure_allocated(&e->child_sources, trivial_hash_func, trivial_compare_func);
- if (r < 0)
- return r;
-
- if (hashmap_contains(e->child_sources, INT_TO_PTR(pid)))
- return -EBUSY;
-
- s = source_new(e, SOURCE_CHILD);
- if (!s)
- return -ENOMEM;
-
- s->child.pid = pid;
- s->child.options = options;
- s->child.callback = callback;
- s->userdata = userdata;
- s->enabled = SD_EVENT_ONESHOT;
-
- r = hashmap_put(e->child_sources, INT_TO_PTR(pid), s);
- if (r < 0) {
- source_free(s);
- return r;
- }
-
- e->n_enabled_child_sources ++;
-
- assert_se(sigaddset(&e->sigset, SIGCHLD) == 0);
-
- if (!e->signal_sources || !e->signal_sources[SIGCHLD]) {
- r = event_update_signal_fd(e);
- if (r < 0) {
- source_free(s);
- return -errno;
- }
- }
-
- e->need_process_child = true;
-
- *ret = s;
- return 0;
-}
-
-_public_ int sd_event_add_defer(
- sd_event *e,
- sd_event_handler_t callback,
- void *userdata,
- sd_event_source **ret) {
-
- sd_event_source *s;
- int r;
-
- assert_return(e, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- s = source_new(e, SOURCE_DEFER);
- if (!s)
- return -ENOMEM;
-
- s->defer.callback = callback;
- s->userdata = userdata;
- s->enabled = SD_EVENT_ONESHOT;
-
- r = source_set_pending(s, true);
- if (r < 0) {
- source_free(s);
- return r;
- }
-
- *ret = s;
- return 0;
-}
-
-_public_ int sd_event_add_exit(
- sd_event *e,
- sd_event_handler_t callback,
- void *userdata,
- sd_event_source **ret) {
-
- sd_event_source *s;
- int r;
-
- assert_return(e, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- if (!e->exit) {
- e->exit = prioq_new(exit_prioq_compare);
- if (!e->exit)
- return -ENOMEM;
- }
-
- s = source_new(e, SOURCE_EXIT);
- if (!s)
- return -ENOMEM;
-
- s->exit.callback = callback;
- s->userdata = userdata;
- s->exit.prioq_index = PRIOQ_IDX_NULL;
- s->enabled = SD_EVENT_ONESHOT;
-
- r = prioq_put(s->event->exit, s, &s->exit.prioq_index);
- if (r < 0) {
- source_free(s);
- return r;
- }
-
- *ret = s;
- return 0;
-}
-
-_public_ sd_event_source* sd_event_source_ref(sd_event_source *s) {
- assert_return(s, NULL);
-
- assert(s->n_ref >= 1);
- s->n_ref++;
-
- return s;
-}
-
-_public_ sd_event_source* sd_event_source_unref(sd_event_source *s) {
-
- if (!s)
- return NULL;
-
- assert(s->n_ref >= 1);
- s->n_ref--;
-
- if (s->n_ref <= 0) {
- /* Here's a special hack: when we are called from a
- * dispatch handler we won't free the event source
- * immediately, but we will detach the fd from the
- * epoll. This way it is safe for the caller to unref
- * the event source and immediately close the fd, but
- * we still retain a valid event source object after
- * the callback. */
-
- if (s->dispatching) {
- if (s->type == SOURCE_IO)
- source_io_unregister(s);
- } else
- source_free(s);
- }
-
- return NULL;
-}
-
-_public_ sd_event *sd_event_source_get_event(sd_event_source *s) {
- assert_return(s, NULL);
-
- return s->event;
-}
-
-_public_ int sd_event_source_get_pending(sd_event_source *s) {
- assert_return(s, -EINVAL);
- assert_return(s->type != SOURCE_EXIT, -EDOM);
- assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- return s->pending;
-}
-
-_public_ int sd_event_source_get_io_fd(sd_event_source *s) {
- assert_return(s, -EINVAL);
- assert_return(s->type == SOURCE_IO, -EDOM);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- return s->io.fd;
-}
-
-_public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
- int r;
-
- assert_return(s, -EINVAL);
- assert_return(fd >= 0, -EINVAL);
- assert_return(s->type == SOURCE_IO, -EDOM);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- if (s->io.fd == fd)
- return 0;
-
- if (s->enabled == SD_EVENT_OFF) {
- s->io.fd = fd;
- s->io.registered = false;
- } else {
- int saved_fd;
-
- saved_fd = s->io.fd;
- assert(s->io.registered);
-
- s->io.fd = fd;
- s->io.registered = false;
-
- r = source_io_register(s, s->enabled, s->io.events);
- if (r < 0) {
- s->io.fd = saved_fd;
- s->io.registered = true;
- return r;
- }
-
- epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL);
- }
-
- return 0;
-}
-
-_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) {
- assert_return(s, -EINVAL);
- assert_return(events, -EINVAL);
- assert_return(s->type == SOURCE_IO, -EDOM);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- *events = s->io.events;
- return 0;
-}
-
-_public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) {
- int r;
-
- assert_return(s, -EINVAL);
- assert_return(s->type == SOURCE_IO, -EDOM);
- assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
- assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- if (s->io.events == events)
- return 0;
-
- if (s->enabled != SD_EVENT_OFF) {
- r = source_io_register(s, s->enabled, events);
- if (r < 0)
- return r;
- }
-
- s->io.events = events;
- source_set_pending(s, false);
-
- return 0;
-}
-
-_public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents) {
- assert_return(s, -EINVAL);
- assert_return(revents, -EINVAL);
- assert_return(s->type == SOURCE_IO, -EDOM);
- assert_return(s->pending, -ENODATA);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- *revents = s->io.revents;
- return 0;
-}
-
-_public_ int sd_event_source_get_signal(sd_event_source *s) {
- assert_return(s, -EINVAL);
- assert_return(s->type == SOURCE_SIGNAL, -EDOM);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- return s->signal.sig;
-}
-
-_public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority) {
- assert_return(s, -EINVAL);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- return s->priority;
-}
-
-_public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) {
- assert_return(s, -EINVAL);
- assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- if (s->priority == priority)
- return 0;
-
- s->priority = priority;
-
- if (s->pending)
- prioq_reshuffle(s->event->pending, s, &s->pending_index);
-
- if (s->prepare)
- prioq_reshuffle(s->event->prepare, s, &s->prepare_index);
-
- if (s->type == SOURCE_EXIT)
- prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index);
-
- return 0;
-}
-
-_public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) {
- assert_return(s, -EINVAL);
- assert_return(m, -EINVAL);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- *m = s->enabled;
- return 0;
-}
-
-_public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
- int r;
-
- assert_return(s, -EINVAL);
- assert_return(m == SD_EVENT_OFF || m == SD_EVENT_ON || m == SD_EVENT_ONESHOT, -EINVAL);
- assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- if (s->enabled == m)
- return 0;
-
- if (m == SD_EVENT_OFF) {
-
- switch (s->type) {
-
- case SOURCE_IO:
- r = source_io_unregister(s);
- if (r < 0)
- return r;
-
- s->enabled = m;
- break;
-
- case SOURCE_MONOTONIC:
- s->enabled = m;
- prioq_reshuffle(s->event->monotonic_earliest, s, &s->time.earliest_index);
- prioq_reshuffle(s->event->monotonic_latest, s, &s->time.latest_index);
- break;
-
- case SOURCE_REALTIME:
- s->enabled = m;
- prioq_reshuffle(s->event->realtime_earliest, s, &s->time.earliest_index);
- prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index);
- break;
-
- case SOURCE_SIGNAL:
- s->enabled = m;
- if (s->signal.sig != SIGCHLD || s->event->n_enabled_child_sources == 0) {
- assert_se(sigdelset(&s->event->sigset, s->signal.sig) == 0);
- event_update_signal_fd(s->event);
- }
-
- break;
-
- case SOURCE_CHILD:
- s->enabled = m;
-
- assert(s->event->n_enabled_child_sources > 0);
- s->event->n_enabled_child_sources--;
-
- if (!s->event->signal_sources || !s->event->signal_sources[SIGCHLD]) {
- assert_se(sigdelset(&s->event->sigset, SIGCHLD) == 0);
- event_update_signal_fd(s->event);
- }
-
- break;
-
- case SOURCE_EXIT:
- s->enabled = m;
- prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index);
- break;
-
- case SOURCE_DEFER:
- s->enabled = m;
- break;
-
- case SOURCE_WATCHDOG:
- assert_not_reached("Wut? I shouldn't exist.");
- }
-
- } else {
- switch (s->type) {
-
- case SOURCE_IO:
- r = source_io_register(s, m, s->io.events);
- if (r < 0)
- return r;
-
- s->enabled = m;
- break;
-
- case SOURCE_MONOTONIC:
- s->enabled = m;
- prioq_reshuffle(s->event->monotonic_earliest, s, &s->time.earliest_index);
- prioq_reshuffle(s->event->monotonic_latest, s, &s->time.latest_index);
- break;
-
- case SOURCE_REALTIME:
- s->enabled = m;
- prioq_reshuffle(s->event->realtime_earliest, s, &s->time.earliest_index);
- prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index);
- break;
-
- case SOURCE_SIGNAL:
- s->enabled = m;
-
- if (s->signal.sig != SIGCHLD || s->event->n_enabled_child_sources == 0) {
- assert_se(sigaddset(&s->event->sigset, s->signal.sig) == 0);
- event_update_signal_fd(s->event);
- }
- break;
-
- case SOURCE_CHILD:
- s->enabled = m;
-
- if (s->enabled == SD_EVENT_OFF) {
- s->event->n_enabled_child_sources++;
-
- if (!s->event->signal_sources || !s->event->signal_sources[SIGCHLD]) {
- assert_se(sigaddset(&s->event->sigset, SIGCHLD) == 0);
- event_update_signal_fd(s->event);
- }
- }
- break;
-
- case SOURCE_EXIT:
- s->enabled = m;
- prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index);
- break;
-
- case SOURCE_DEFER:
- s->enabled = m;
- break;
-
- case SOURCE_WATCHDOG:
- assert_not_reached("Wut? I shouldn't exist.");
- }
- }
-
- if (s->pending)
- prioq_reshuffle(s->event->pending, s, &s->pending_index);
-
- if (s->prepare)
- prioq_reshuffle(s->event->prepare, s, &s->prepare_index);
-
- return 0;
-}
-
-_public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) {
- assert_return(s, -EINVAL);
- assert_return(usec, -EINVAL);
- assert_return(s->type == SOURCE_REALTIME || s->type == SOURCE_MONOTONIC, -EDOM);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- *usec = s->time.next;
- return 0;
-}
-
-_public_ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) {
- assert_return(s, -EINVAL);
- assert_return(usec != (uint64_t) -1, -EINVAL);
- assert_return(s->type == SOURCE_REALTIME || s->type == SOURCE_MONOTONIC, -EDOM);
- assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- s->time.next = usec;
-
- source_set_pending(s, false);
-
- if (s->type == SOURCE_REALTIME) {
- prioq_reshuffle(s->event->realtime_earliest, s, &s->time.earliest_index);
- prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index);
- } else {
- prioq_reshuffle(s->event->monotonic_earliest, s, &s->time.earliest_index);
- prioq_reshuffle(s->event->monotonic_latest, s, &s->time.latest_index);
- }
-
- return 0;
-}
-
-_public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) {
- assert_return(s, -EINVAL);
- assert_return(usec, -EINVAL);
- assert_return(s->type == SOURCE_REALTIME || s->type == SOURCE_MONOTONIC, -EDOM);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- *usec = s->time.accuracy;
- return 0;
-}
-
-_public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec) {
- assert_return(s, -EINVAL);
- assert_return(usec != (uint64_t) -1, -EINVAL);
- assert_return(s->type == SOURCE_REALTIME || s->type == SOURCE_MONOTONIC, -EDOM);
- assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- if (usec == 0)
- usec = DEFAULT_ACCURACY_USEC;
-
- s->time.accuracy = usec;
-
- source_set_pending(s, false);
-
- if (s->type == SOURCE_REALTIME)
- prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index);
- else
- prioq_reshuffle(s->event->monotonic_latest, s, &s->time.latest_index);
-
- return 0;
-}
-
-_public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) {
- assert_return(s, -EINVAL);
- assert_return(pid, -EINVAL);
- assert_return(s->type == SOURCE_CHILD, -EDOM);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- *pid = s->child.pid;
- return 0;
-}
-
-_public_ int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback) {
- int r;
-
- assert_return(s, -EINVAL);
- assert_return(s->type != SOURCE_EXIT, -EDOM);
- assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(s->event), -ECHILD);
-
- if (s->prepare == callback)
- return 0;
-
- if (callback && s->prepare) {
- s->prepare = callback;
- return 0;
- }
-
- r = prioq_ensure_allocated(&s->event->prepare, prepare_prioq_compare);
- if (r < 0)
- return r;
-
- s->prepare = callback;
-
- if (callback) {
- r = prioq_put(s->event->prepare, s, &s->prepare_index);
- if (r < 0)
- return r;
- } else
- prioq_remove(s->event->prepare, s, &s->prepare_index);
-
- return 0;
-}
-
-_public_ void* sd_event_source_get_userdata(sd_event_source *s) {
- assert_return(s, NULL);
-
- return s->userdata;
-}
-
-_public_ void *sd_event_source_set_userdata(sd_event_source *s, void *userdata) {
- void *ret;
-
- assert_return(s, NULL);
-
- ret = s->userdata;
- s->userdata = userdata;
-
- return ret;
-}
-
-static usec_t sleep_between(sd_event *e, usec_t a, usec_t b) {
- usec_t c;
- assert(e);
- assert(a <= b);
-
- if (a <= 0)
- return 0;
-
- if (b <= a + 1)
- return a;
-
- /*
- Find a good time to wake up again between times a and b. We
- have two goals here:
-
- a) We want to wake up as seldom as possible, hence prefer
- later times over earlier times.
-
- b) But if we have to wake up, then let's make sure to
- dispatch as much as possible on the entire system.
-
- We implement this by waking up everywhere at the same time
- within any given minute if we can, synchronised via the
- perturbation value determined from the boot ID. If we can't,
- then we try to find the same spot in every 10s, then 1s and
- then 250ms step. Otherwise, we pick the last possible time
- to wake up.
- */
-
- c = (b / USEC_PER_MINUTE) * USEC_PER_MINUTE + e->perturb;
- if (c >= b) {
- if (_unlikely_(c < USEC_PER_MINUTE))
- return b;
-
- c -= USEC_PER_MINUTE;
- }
-
- if (c >= a)
- return c;
-
- c = (b / (USEC_PER_SEC*10)) * (USEC_PER_SEC*10) + (e->perturb % (USEC_PER_SEC*10));
- if (c >= b) {
- if (_unlikely_(c < USEC_PER_SEC*10))
- return b;
-
- c -= USEC_PER_SEC*10;
- }
-
- if (c >= a)
- return c;
-
- c = (b / USEC_PER_SEC) * USEC_PER_SEC + (e->perturb % USEC_PER_SEC);
- if (c >= b) {
- if (_unlikely_(c < USEC_PER_SEC))
- return b;
-
- c -= USEC_PER_SEC;
- }
-
- if (c >= a)
- return c;
-
- c = (b / (USEC_PER_MSEC*250)) * (USEC_PER_MSEC*250) + (e->perturb % (USEC_PER_MSEC*250));
- if (c >= b) {
- if (_unlikely_(c < USEC_PER_MSEC*250))
- return b;
-
- c -= USEC_PER_MSEC*250;
- }
-
- if (c >= a)
- return c;
-
- return b;
-}
-
-static int event_arm_timer(
- sd_event *e,
- int timer_fd,
- Prioq *earliest,
- Prioq *latest,
- usec_t *next) {
-
- struct itimerspec its = {};
- sd_event_source *a, *b;
- usec_t t;
- int r;
-
- assert(e);
- assert(next);
-
- a = prioq_peek(earliest);
- if (!a || a->enabled == SD_EVENT_OFF) {
-
- if (timer_fd < 0)
- return 0;
-
- if (*next == (usec_t) -1)
- return 0;
-
- /* disarm */
- r = timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &its, NULL);
- if (r < 0)
- return r;
-
- *next = (usec_t) -1;
-
- return 0;
- }
-
- b = prioq_peek(latest);
- assert_se(b && b->enabled != SD_EVENT_OFF);
-
- t = sleep_between(e, a->time.next, b->time.next + b->time.accuracy);
- if (*next == t)
- return 0;
-
- assert_se(timer_fd >= 0);
-
- if (t == 0) {
- /* We don' want to disarm here, just mean some time looooong ago. */
- its.it_value.tv_sec = 0;
- its.it_value.tv_nsec = 1;
- } else
- timespec_store(&its.it_value, t);
-
- r = timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &its, NULL);
- if (r < 0)
- return -errno;
-
- *next = t;
- return 0;
-}
-
-static int process_io(sd_event *e, sd_event_source *s, uint32_t revents) {
- assert(e);
- assert(s);
- assert(s->type == SOURCE_IO);
-
- /* If the event source was already pending, we just OR in the
- * new revents, otherwise we reset the value. The ORing is
- * necessary to handle EPOLLONESHOT events properly where
- * readability might happen independently of writability, and
- * we need to keep track of both */
-
- if (s->pending)
- s->io.revents |= revents;
- else
- s->io.revents = revents;
-
- return source_set_pending(s, true);
-}
-
-static int flush_timer(sd_event *e, int fd, uint32_t events, usec_t *next) {
- uint64_t x;
- ssize_t ss;
-
- assert(e);
- assert(fd >= 0);
-
- assert_return(events == EPOLLIN, -EIO);
-
- ss = read(fd, &x, sizeof(x));
- if (ss < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
-
- return -errno;
- }
-
- if (_unlikely_(ss != sizeof(x)))
- return -EIO;
-
- if (next)
- *next = (usec_t) -1;
-
- return 0;
-}
-
-static int process_timer(
- sd_event *e,
- usec_t n,
- Prioq *earliest,
- Prioq *latest) {
-
- sd_event_source *s;
- int r;
-
- assert(e);
-
- for (;;) {
- s = prioq_peek(earliest);
- if (!s ||
- s->time.next > n ||
- s->enabled == SD_EVENT_OFF ||
- s->pending)
- break;
-
- r = source_set_pending(s, true);
- if (r < 0)
- return r;
-
- prioq_reshuffle(earliest, s, &s->time.earliest_index);
- prioq_reshuffle(latest, s, &s->time.latest_index);
- }
-
- return 0;
-}
-
-static int process_child(sd_event *e) {
- sd_event_source *s;
- Iterator i;
- int r;
-
- assert(e);
-
- e->need_process_child = false;
-
- /*
- So, this is ugly. We iteratively invoke waitid() with P_PID
- + WNOHANG for each PID we wait for, instead of using
- P_ALL. This is because we only want to get child
- information of very specific child processes, and not all
- of them. We might not have processed the SIGCHLD even of a
- previous invocation and we don't want to maintain a
- unbounded *per-child* event queue, hence we really don't
- want anything flushed out of the kernel's queue that we
- don't care about. Since this is O(n) this means that if you
- have a lot of processes you probably want to handle SIGCHLD
- yourself.
-
- We do not reap the children here (by using WNOWAIT), this
- is only done after the event source is dispatched so that
- the callback still sees the process as a zombie.
- */
-
- HASHMAP_FOREACH(s, e->child_sources, i) {
- assert(s->type == SOURCE_CHILD);
-
- if (s->pending)
- continue;
-
- if (s->enabled == SD_EVENT_OFF)
- continue;
-
- zero(s->child.siginfo);
- r = waitid(P_PID, s->child.pid, &s->child.siginfo,
- WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options);
- if (r < 0)
- return -errno;
-
- if (s->child.siginfo.si_pid != 0) {
- bool zombie =
- s->child.siginfo.si_code == CLD_EXITED ||
- s->child.siginfo.si_code == CLD_KILLED ||
- s->child.siginfo.si_code == CLD_DUMPED;
-
- if (!zombie && (s->child.options & WEXITED)) {
- /* If the child isn't dead then let's
- * immediately remove the state change
- * from the queue, since there's no
- * benefit in leaving it queued */
-
- assert(s->child.options & (WSTOPPED|WCONTINUED));
- waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
- }
-
- r = source_set_pending(s, true);
- if (r < 0)
- return r;
- }
- }
-
- return 0;
-}
-
-static int process_signal(sd_event *e, uint32_t events) {
- bool read_one = false;
- int r;
-
- assert(e);
- assert(e->signal_sources);
-
- assert_return(events == EPOLLIN, -EIO);
-
- for (;;) {
- struct signalfd_siginfo si;
- ssize_t ss;
- sd_event_source *s;
-
- ss = read(e->signal_fd, &si, sizeof(si));
- if (ss < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return read_one;
-
- return -errno;
- }
-
- if (_unlikely_(ss != sizeof(si)))
- return -EIO;
-
- read_one = true;
-
- s = e->signal_sources[si.ssi_signo];
- if (si.ssi_signo == SIGCHLD) {
- r = process_child(e);
- if (r < 0)
- return r;
- if (r > 0 || !s)
- continue;
- } else
- if (!s)
- return -EIO;
-
- s->signal.siginfo = si;
- r = source_set_pending(s, true);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int source_dispatch(sd_event_source *s) {
- int r = 0;
-
- assert(s);
- assert(s->pending || s->type == SOURCE_EXIT);
-
- if (s->type != SOURCE_DEFER && s->type != SOURCE_EXIT) {
- r = source_set_pending(s, false);
- if (r < 0)
- return r;
- }
-
- if (s->enabled == SD_EVENT_ONESHOT) {
- r = sd_event_source_set_enabled(s, SD_EVENT_OFF);
- if (r < 0)
- return r;
- }
-
- s->dispatching = true;
-
- switch (s->type) {
-
- case SOURCE_IO:
- r = s->io.callback(s, s->io.fd, s->io.revents, s->userdata);
- break;
-
- case SOURCE_MONOTONIC:
- r = s->time.callback(s, s->time.next, s->userdata);
- break;
-
- case SOURCE_REALTIME:
- r = s->time.callback(s, s->time.next, s->userdata);
- break;
-
- case SOURCE_SIGNAL:
- r = s->signal.callback(s, &s->signal.siginfo, s->userdata);
- break;
-
- case SOURCE_CHILD: {
- bool zombie;
-
- zombie = s->child.siginfo.si_code == CLD_EXITED ||
- s->child.siginfo.si_code == CLD_KILLED ||
- s->child.siginfo.si_code == CLD_DUMPED;
-
- r = s->child.callback(s, &s->child.siginfo, s->userdata);
-
- /* Now, reap the PID for good. */
- if (zombie)
- waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED);
-
- break;
- }
-
- case SOURCE_DEFER:
- r = s->defer.callback(s, s->userdata);
- break;
-
- case SOURCE_EXIT:
- r = s->exit.callback(s, s->userdata);
- break;
-
- case SOURCE_WATCHDOG:
- assert_not_reached("Wut? I shouldn't exist.");
- }
-
- s->dispatching = false;
-
- if (r < 0)
- log_debug("Event source %p returned error, disabling: %s", s, strerror(-r));
-
- if (s->n_ref == 0)
- source_free(s);
- else if (r < 0)
- sd_event_source_set_enabled(s, SD_EVENT_OFF);
-
- return 1;
-}
-
-static int event_prepare(sd_event *e) {
- int r;
-
- assert(e);
-
- for (;;) {
- sd_event_source *s;
-
- s = prioq_peek(e->prepare);
- if (!s || s->prepare_iteration == e->iteration || s->enabled == SD_EVENT_OFF)
- break;
-
- s->prepare_iteration = e->iteration;
- r = prioq_reshuffle(e->prepare, s, &s->prepare_index);
- if (r < 0)
- return r;
-
- assert(s->prepare);
-
- s->dispatching = true;
- r = s->prepare(s, s->userdata);
- s->dispatching = false;
-
- if (r < 0)
- log_debug("Prepare callback of event source %p returned error, disabling: %s", s, strerror(-r));
-
- if (s->n_ref == 0)
- source_free(s);
- else if (r < 0)
- sd_event_source_set_enabled(s, SD_EVENT_OFF);
- }
-
- return 0;
-}
-
-static int dispatch_exit(sd_event *e) {
- sd_event_source *p;
- int r;
-
- assert(e);
-
- p = prioq_peek(e->exit);
- if (!p || p->enabled == SD_EVENT_OFF) {
- e->state = SD_EVENT_FINISHED;
- return 0;
- }
-
- sd_event_ref(e);
- e->iteration++;
- e->state = SD_EVENT_EXITING;
-
- r = source_dispatch(p);
-
- e->state = SD_EVENT_PASSIVE;
- sd_event_unref(e);
-
- return r;
-}
-
-static sd_event_source* event_next_pending(sd_event *e) {
- sd_event_source *p;
-
- assert(e);
-
- p = prioq_peek(e->pending);
- if (!p)
- return NULL;
-
- if (p->enabled == SD_EVENT_OFF)
- return NULL;
-
- return p;
-}
-
-static int arm_watchdog(sd_event *e) {
- struct itimerspec its = {};
- usec_t t;
- int r;
-
- assert(e);
- assert(e->watchdog_fd >= 0);
-
- t = sleep_between(e,
- e->watchdog_last + (e->watchdog_period / 2),
- e->watchdog_last + (e->watchdog_period * 3 / 4));
-
- timespec_store(&its.it_value, t);
-
- r = timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-static int process_watchdog(sd_event *e) {
- assert(e);
-
- if (!e->watchdog)
- return 0;
-
- /* Don't notify watchdog too often */
- if (e->watchdog_last + e->watchdog_period / 4 > e->timestamp.monotonic)
- return 0;
-
- sd_notify(false, "WATCHDOG=1");
- e->watchdog_last = e->timestamp.monotonic;
-
- return arm_watchdog(e);
-}
-
-_public_ int sd_event_run(sd_event *e, uint64_t timeout) {
- struct epoll_event *ev_queue;
- unsigned ev_queue_max;
- sd_event_source *p;
- int r, i, m;
-
- assert_return(e, -EINVAL);
- assert_return(!event_pid_changed(e), -ECHILD);
- assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY);
-
- if (e->exit_requested)
- return dispatch_exit(e);
-
- sd_event_ref(e);
- e->iteration++;
- e->state = SD_EVENT_RUNNING;
-
- r = event_prepare(e);
- if (r < 0)
- goto finish;
-
- r = event_arm_timer(e, e->monotonic_fd, e->monotonic_earliest, e->monotonic_latest, &e->monotonic_next);
- if (r < 0)
- goto finish;
-
- r = event_arm_timer(e, e->realtime_fd, e->realtime_earliest, e->realtime_latest, &e->realtime_next);
- if (r < 0)
- goto finish;
-
- if (event_next_pending(e) || e->need_process_child)
- timeout = 0;
- ev_queue_max = CLAMP(e->n_sources, 1U, EPOLL_QUEUE_MAX);
- ev_queue = newa(struct epoll_event, ev_queue_max);
-
- m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max,
- timeout == (uint64_t) -1 ? -1 : (int) ((timeout + USEC_PER_MSEC - 1) / USEC_PER_MSEC));
- if (m < 0) {
- r = errno == EAGAIN || errno == EINTR ? 1 : -errno;
- goto finish;
- }
-
- dual_timestamp_get(&e->timestamp);
-
- for (i = 0; i < m; i++) {
-
- if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_MONOTONIC))
- r = flush_timer(e, e->monotonic_fd, ev_queue[i].events, &e->monotonic_next);
- else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_REALTIME))
- r = flush_timer(e, e->realtime_fd, ev_queue[i].events, &e->realtime_next);
- else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_SIGNAL))
- r = process_signal(e, ev_queue[i].events);
- else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG))
- r = flush_timer(e, e->watchdog_fd, ev_queue[i].events, NULL);
- else
- r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events);
-
- if (r < 0)
- goto finish;
- }
-
- r = process_watchdog(e);
- if (r < 0)
- goto finish;
-
- r = process_timer(e, e->timestamp.monotonic, e->monotonic_earliest, e->monotonic_latest);
- if (r < 0)
- goto finish;
-
- r = process_timer(e, e->timestamp.realtime, e->realtime_earliest, e->realtime_latest);
- if (r < 0)
- goto finish;
-
- if (e->need_process_child) {
- r = process_child(e);
- if (r < 0)
- goto finish;
- }
-
- p = event_next_pending(e);
- if (!p) {
- r = 1;
- goto finish;
- }
-
- r = source_dispatch(p);
-
-finish:
- e->state = SD_EVENT_PASSIVE;
- sd_event_unref(e);
-
- return r;
-}
-
-_public_ int sd_event_loop(sd_event *e) {
- int r;
-
- assert_return(e, -EINVAL);
- assert_return(!event_pid_changed(e), -ECHILD);
- assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY);
-
- sd_event_ref(e);
-
- while (e->state != SD_EVENT_FINISHED) {
- r = sd_event_run(e, (uint64_t) -1);
- if (r < 0)
- goto finish;
- }
-
- r = e->exit_code;
-
-finish:
- sd_event_unref(e);
- return r;
-}
-
-_public_ int sd_event_get_state(sd_event *e) {
- assert_return(e, -EINVAL);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- return e->state;
-}
-
-_public_ int sd_event_get_exit_code(sd_event *e, int *code) {
- assert_return(e, -EINVAL);
- assert_return(code, -EINVAL);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- if (!e->exit_requested)
- return -ENODATA;
-
- *code = e->exit_code;
- return 0;
-}
-
-_public_ int sd_event_exit(sd_event *e, int code) {
- assert_return(e, -EINVAL);
- assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- e->exit_requested = true;
- e->exit_code = code;
-
- return 0;
-}
-
-_public_ int sd_event_get_now_realtime(sd_event *e, uint64_t *usec) {
- assert_return(e, -EINVAL);
- assert_return(usec, -EINVAL);
- assert_return(dual_timestamp_is_set(&e->timestamp), -ENODATA);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- *usec = e->timestamp.realtime;
- return 0;
-}
-
-_public_ int sd_event_get_now_monotonic(sd_event *e, uint64_t *usec) {
- assert_return(e, -EINVAL);
- assert_return(usec, -EINVAL);
- assert_return(dual_timestamp_is_set(&e->timestamp), -ENODATA);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- *usec = e->timestamp.monotonic;
- return 0;
-}
-
-_public_ int sd_event_default(sd_event **ret) {
-
- static thread_local sd_event *default_event = NULL;
- sd_event *e;
- int r;
-
- if (!ret)
- return !!default_event;
-
- if (default_event) {
- *ret = sd_event_ref(default_event);
- return 0;
- }
-
- r = sd_event_new(&e);
- if (r < 0)
- return r;
-
- e->default_event_ptr = &default_event;
- e->tid = gettid();
- default_event = e;
-
- *ret = e;
- return 1;
-}
-
-_public_ int sd_event_get_tid(sd_event *e, pid_t *tid) {
- assert_return(e, -EINVAL);
- assert_return(tid, -EINVAL);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- if (e->tid != 0) {
- *tid = e->tid;
- return 0;
- }
-
- return -ENXIO;
-}
-
-_public_ int sd_event_set_watchdog(sd_event *e, int b) {
- int r;
-
- assert_return(e, -EINVAL);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- if (e->watchdog == !!b)
- return e->watchdog;
-
- if (b) {
- struct epoll_event ev = {};
-
- r = sd_watchdog_enabled(false, &e->watchdog_period);
- if (r <= 0)
- return r;
-
- /* Issue first ping immediately */
- sd_notify(false, "WATCHDOG=1");
- e->watchdog_last = now(CLOCK_MONOTONIC);
-
- e->watchdog_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
- if (e->watchdog_fd < 0)
- return -errno;
-
- r = arm_watchdog(e);
- if (r < 0)
- goto fail;
-
- ev.events = EPOLLIN;
- ev.data.ptr = INT_TO_PTR(SOURCE_WATCHDOG);
-
- r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->watchdog_fd, &ev);
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- } else {
- if (e->watchdog_fd >= 0) {
- epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, e->watchdog_fd, NULL);
- close_nointr_nofail(e->watchdog_fd);
- e->watchdog_fd = -1;
- }
- }
-
- e->watchdog = !!b;
- return e->watchdog;
-
-fail:
- close_nointr_nofail(e->watchdog_fd);
- e->watchdog_fd = -1;
- return r;
-}
-
-_public_ int sd_event_get_watchdog(sd_event *e) {
- assert_return(e, -EINVAL);
- assert_return(!event_pid_changed(e), -ECHILD);
-
- return e->watchdog;
-}
diff --git a/src/libsystemd/sd-event/Makefile b/src/libsystemd/sd-event/Makefile
new file mode 120000
index 0000000..94aaae2
--- /dev/null
+++ b/src/libsystemd/sd-event/Makefile
@@ -0,0 +1 @@
+../../Makefile
\ No newline at end of file
diff --git a/src/libsystemd/sd-event/event-util.h b/src/libsystemd/sd-event/event-util.h
new file mode 100644
index 0000000..e58020d
--- /dev/null
+++ b/src/libsystemd/sd-event/event-util.h
@@ -0,0 +1,30 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event*, sd_event_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, sd_event_source_unref);
+
+#define _cleanup_event_unref_ _cleanup_(sd_event_unrefp)
+#define _cleanup_event_source_unref_ _cleanup_(sd_event_source_unrefp)
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
new file mode 100644
index 0000000..f33a9ec
--- /dev/null
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -0,0 +1,2224 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <sys/wait.h>
+#include <pthread.h>
+
+#include "sd-id128.h"
+#include "sd-daemon.h"
+#include "macro.h"
+#include "prioq.h"
+#include "hashmap.h"
+#include "util.h"
+#include "time-util.h"
+#include "missing.h"
+
+#include "sd-event.h"
+
+#define EPOLL_QUEUE_MAX 512U
+#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
+
+typedef enum EventSourceType {
+ SOURCE_IO,
+ SOURCE_MONOTONIC,
+ SOURCE_REALTIME,
+ SOURCE_SIGNAL,
+ SOURCE_CHILD,
+ SOURCE_DEFER,
+ SOURCE_EXIT,
+ SOURCE_WATCHDOG
+} EventSourceType;
+
+struct sd_event_source {
+ unsigned n_ref;
+
+ sd_event *event;
+ void *userdata;
+ sd_event_handler_t prepare;
+
+ EventSourceType type:4;
+ int enabled:3;
+ bool pending:1;
+ bool dispatching:1;
+
+ int64_t priority;
+ unsigned pending_index;
+ unsigned prepare_index;
+ unsigned pending_iteration;
+ unsigned prepare_iteration;
+
+ union {
+ struct {
+ sd_event_io_handler_t callback;
+ int fd;
+ uint32_t events;
+ uint32_t revents;
+ bool registered:1;
+ } io;
+ struct {
+ sd_event_time_handler_t callback;
+ usec_t next, accuracy;
+ unsigned earliest_index;
+ unsigned latest_index;
+ } time;
+ struct {
+ sd_event_signal_handler_t callback;
+ struct signalfd_siginfo siginfo;
+ int sig;
+ } signal;
+ struct {
+ sd_event_child_handler_t callback;
+ siginfo_t siginfo;
+ pid_t pid;
+ int options;
+ } child;
+ struct {
+ sd_event_handler_t callback;
+ } defer;
+ struct {
+ sd_event_handler_t callback;
+ unsigned prioq_index;
+ } exit;
+ };
+};
+
+struct sd_event {
+ unsigned n_ref;
+
+ int epoll_fd;
+ int signal_fd;
+ int realtime_fd;
+ int monotonic_fd;
+ int watchdog_fd;
+
+ Prioq *pending;
+ Prioq *prepare;
+
+ /* For both clocks we maintain two priority queues each, one
+ * ordered for the earliest times the events may be
+ * dispatched, and one ordered by the latest times they must
+ * have been dispatched. The range between the top entries in
+ * the two prioqs is the time window we can freely schedule
+ * wakeups in */
+ Prioq *monotonic_earliest;
+ Prioq *monotonic_latest;
+ Prioq *realtime_earliest;
+ Prioq *realtime_latest;
+
+ usec_t realtime_next, monotonic_next;
+ usec_t perturb;
+
+ sigset_t sigset;
+ sd_event_source **signal_sources;
+
+ Hashmap *child_sources;
+ unsigned n_enabled_child_sources;
+
+ Prioq *exit;
+
+ pid_t original_pid;
+
+ unsigned iteration;
+ dual_timestamp timestamp;
+ int state;
+
+ bool exit_requested:1;
+ bool need_process_child:1;
+ bool watchdog:1;
+
+ int exit_code;
+
+ pid_t tid;
+ sd_event **default_event_ptr;
+
+ usec_t watchdog_last, watchdog_period;
+
+ unsigned n_sources;
+};
+
+static int pending_prioq_compare(const void *a, const void *b) {
+ const sd_event_source *x = a, *y = b;
+
+ assert(x->pending);
+ assert(y->pending);
+
+ /* Enabled ones first */
+ if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
+ return -1;
+ if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
+ return 1;
+
+ /* Lower priority values first */
+ if (x->priority < y->priority)
+ return -1;
+ if (x->priority > y->priority)
+ return 1;
+
+ /* Older entries first */
+ if (x->pending_iteration < y->pending_iteration)
+ return -1;
+ if (x->pending_iteration > y->pending_iteration)
+ return 1;
+
+ /* Stability for the rest */
+ if (x < y)
+ return -1;
+ if (x > y)
+ return 1;
+
+ return 0;
+}
+
+static int prepare_prioq_compare(const void *a, const void *b) {
+ const sd_event_source *x = a, *y = b;
+
+ assert(x->prepare);
+ assert(y->prepare);
+
+ /* Move most recently prepared ones last, so that we can stop
+ * preparing as soon as we hit one that has already been
+ * prepared in the current iteration */
+ if (x->prepare_iteration < y->prepare_iteration)
+ return -1;
+ if (x->prepare_iteration > y->prepare_iteration)
+ return 1;
+
+ /* Enabled ones first */
+ if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
+ return -1;
+ if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
+ return 1;
+
+ /* Lower priority values first */
+ if (x->priority < y->priority)
+ return -1;
+ if (x->priority > y->priority)
+ return 1;
+
+ /* Stability for the rest */
+ if (x < y)
+ return -1;
+ if (x > y)
+ return 1;
+
+ return 0;
+}
+
+static int earliest_time_prioq_compare(const void *a, const void *b) {
+ const sd_event_source *x = a, *y = b;
+
+ assert(x->type == SOURCE_MONOTONIC || x->type == SOURCE_REALTIME);
+ assert(y->type == SOURCE_MONOTONIC || y->type == SOURCE_REALTIME);
+
+ /* Enabled ones first */
+ if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
+ return -1;
+ if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
+ return 1;
+
+ /* Move the pending ones to the end */
+ if (!x->pending && y->pending)
+ return -1;
+ if (x->pending && !y->pending)
+ return 1;
+
+ /* Order by time */
+ if (x->time.next < y->time.next)
+ return -1;
+ if (x->time.next > y->time.next)
+ return 1;
+
+ /* Stability for the rest */
+ if (x < y)
+ return -1;
+ if (x > y)
+ return 1;
+
+ return 0;
+}
+
+static int latest_time_prioq_compare(const void *a, const void *b) {
+ const sd_event_source *x = a, *y = b;
+
+ assert((x->type == SOURCE_MONOTONIC && y->type == SOURCE_MONOTONIC) ||
+ (x->type == SOURCE_REALTIME && y->type == SOURCE_REALTIME));
+
+ /* Enabled ones first */
+ if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
+ return -1;
+ if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
+ return 1;
+
+ /* Move the pending ones to the end */
+ if (!x->pending && y->pending)
+ return -1;
+ if (x->pending && !y->pending)
+ return 1;
+
+ /* Order by time */
+ if (x->time.next + x->time.accuracy < y->time.next + y->time.accuracy)
+ return -1;
+ if (x->time.next + x->time.accuracy > y->time.next + y->time.accuracy)
+ return 1;
+
+ /* Stability for the rest */
+ if (x < y)
+ return -1;
+ if (x > y)
+ return 1;
+
+ return 0;
+}
+
+static int exit_prioq_compare(const void *a, const void *b) {
+ const sd_event_source *x = a, *y = b;
+
+ assert(x->type == SOURCE_EXIT);
+ assert(y->type == SOURCE_EXIT);
+
+ /* Enabled ones first */
+ if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
+ return -1;
+ if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
+ return 1;
+
+ /* Lower priority values first */
+ if (x->priority < y->priority)
+ return -1;
+ if (x->priority > y->priority)
+ return 1;
+
+ /* Stability for the rest */
+ if (x < y)
+ return -1;
+ if (x > y)
+ return 1;
+
+ return 0;
+}
+
+static void event_free(sd_event *e) {
+ assert(e);
+ assert(e->n_sources == 0);
+
+ if (e->default_event_ptr)
+ *(e->default_event_ptr) = NULL;
+
+ if (e->epoll_fd >= 0)
+ close_nointr_nofail(e->epoll_fd);
+
+ if (e->signal_fd >= 0)
+ close_nointr_nofail(e->signal_fd);
+
+ if (e->realtime_fd >= 0)
+ close_nointr_nofail(e->realtime_fd);
+
+ if (e->monotonic_fd >= 0)
+ close_nointr_nofail(e->monotonic_fd);
+
+ if (e->watchdog_fd >= 0)
+ close_nointr_nofail(e->watchdog_fd);
+
+ prioq_free(e->pending);
+ prioq_free(e->prepare);
+ prioq_free(e->monotonic_earliest);
+ prioq_free(e->monotonic_latest);
+ prioq_free(e->realtime_earliest);
+ prioq_free(e->realtime_latest);
+ prioq_free(e->exit);
+
+ free(e->signal_sources);
+
+ hashmap_free(e->child_sources);
+ free(e);
+}
+
+_public_ int sd_event_new(sd_event** ret) {
+ sd_event *e;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ e = new0(sd_event, 1);
+ if (!e)
+ return -ENOMEM;
+
+ e->n_ref = 1;
+ e->signal_fd = e->realtime_fd = e->monotonic_fd = e->watchdog_fd = e->epoll_fd = -1;
+ e->realtime_next = e->monotonic_next = (usec_t) -1;
+ e->original_pid = getpid();
+
+ assert_se(sigemptyset(&e->sigset) == 0);
+
+ e->pending = prioq_new(pending_prioq_compare);
+ if (!e->pending) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ e->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (e->epoll_fd < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ *ret = e;
+ return 0;
+
+fail:
+ event_free(e);
+ return r;
+}
+
+_public_ sd_event* sd_event_ref(sd_event *e) {
+ assert_return(e, NULL);
+
+ assert(e->n_ref >= 1);
+ e->n_ref++;
+
+ return e;
+}
+
+_public_ sd_event* sd_event_unref(sd_event *e) {
+
+ if (!e)
+ return NULL;
+
+ assert(e->n_ref >= 1);
+ e->n_ref--;
+
+ if (e->n_ref <= 0)
+ event_free(e);
+
+ return NULL;
+}
+
+static bool event_pid_changed(sd_event *e) {
+ assert(e);
+
+ /* We don't support people creating am event loop and keeping
+ * it around over a fork(). Let's complain. */
+
+ return e->original_pid != getpid();
+}
+
+static int source_io_unregister(sd_event_source *s) {
+ int r;
+
+ assert(s);
+ assert(s->type == SOURCE_IO);
+
+ if (!s->io.registered)
+ return 0;
+
+ r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL);
+ if (r < 0)
+ return -errno;
+
+ s->io.registered = false;
+ return 0;
+}
+
+static int source_io_register(
+ sd_event_source *s,
+ int enabled,
+ uint32_t events) {
+
+ struct epoll_event ev = {};
+ int r;
+
+ assert(s);
+ assert(s->type == SOURCE_IO);
+ assert(enabled != SD_EVENT_OFF);
+
+ ev.events = events;
+ ev.data.ptr = s;
+
+ if (enabled == SD_EVENT_ONESHOT)
+ ev.events |= EPOLLONESHOT;
+
+ if (s->io.registered)
+ r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_MOD, s->io.fd, &ev);
+ else
+ r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_ADD, s->io.fd, &ev);
+
+ if (r < 0)
+ return -errno;
+
+ s->io.registered = true;
+
+ return 0;
+}
+
+static void source_free(sd_event_source *s) {
+ assert(s);
+
+ if (s->event) {
+ assert(s->event->n_sources > 0);
+
+ switch (s->type) {
+
+ case SOURCE_IO:
+ if (s->io.fd >= 0)
+ source_io_unregister(s);
+
+ break;
+
+ case SOURCE_MONOTONIC:
+ prioq_remove(s->event->monotonic_earliest, s, &s->time.earliest_index);
+ prioq_remove(s->event->monotonic_latest, s, &s->time.latest_index);
+ break;
+
+ case SOURCE_REALTIME:
+ prioq_remove(s->event->realtime_earliest, s, &s->time.earliest_index);
+ prioq_remove(s->event->realtime_latest, s, &s->time.latest_index);
+ break;
+
+ case SOURCE_SIGNAL:
+ if (s->signal.sig > 0) {
+ if (s->signal.sig != SIGCHLD || s->event->n_enabled_child_sources == 0)
+ assert_se(sigdelset(&s->event->sigset, s->signal.sig) == 0);
+
+ if (s->event->signal_sources)
+ s->event->signal_sources[s->signal.sig] = NULL;
+ }
+
+ break;
+
+ case SOURCE_CHILD:
+ if (s->child.pid > 0) {
+ if (s->enabled != SD_EVENT_OFF) {
+ assert(s->event->n_enabled_child_sources > 0);
+ s->event->n_enabled_child_sources--;
+ }
+
+ if (!s->event->signal_sources || !s->event->signal_sources[SIGCHLD])
+ assert_se(sigdelset(&s->event->sigset, SIGCHLD) == 0);
+
+ hashmap_remove(s->event->child_sources, INT_TO_PTR(s->child.pid));
+ }
+
+ break;
+
+ case SOURCE_DEFER:
+ /* nothing */
+ break;
+
+ case SOURCE_EXIT:
+ prioq_remove(s->event->exit, s, &s->exit.prioq_index);
+ break;
+
+ case SOURCE_WATCHDOG:
+ assert_not_reached("Wut? I shouldn't exist.");
+ }
+
+ if (s->pending)
+ prioq_remove(s->event->pending, s, &s->pending_index);
+
+ if (s->prepare)
+ prioq_remove(s->event->prepare, s, &s->prepare_index);
+
+ s->event->n_sources--;
+ sd_event_unref(s->event);
+ }
+
+ free(s);
+}
+
+static int source_set_pending(sd_event_source *s, bool b) {
+ int r;
+
+ assert(s);
+ assert(s->type != SOURCE_EXIT);
+
+ if (s->pending == b)
+ return 0;
+
+ s->pending = b;
+
+ if (b) {
+ s->pending_iteration = s->event->iteration;
+
+ r = prioq_put(s->event->pending, s, &s->pending_index);
+ if (r < 0) {
+ s->pending = false;
+ return r;
+ }
+ } else
+ assert_se(prioq_remove(s->event->pending, s, &s->pending_index));
+
+ if (s->type == SOURCE_REALTIME) {
+ prioq_reshuffle(s->event->realtime_earliest, s, &s->time.earliest_index);
+ prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index);
+ } else if (s->type == SOURCE_MONOTONIC) {
+ prioq_reshuffle(s->event->monotonic_earliest, s, &s->time.earliest_index);
+ prioq_reshuffle(s->event->monotonic_latest, s, &s->time.latest_index);
+ }
+
+ return 0;
+}
+
+static sd_event_source *source_new(sd_event *e, EventSourceType type) {
+ sd_event_source *s;
+
+ assert(e);
+
+ s = new0(sd_event_source, 1);
+ if (!s)
+ return NULL;
+
+ s->n_ref = 1;
+ s->event = sd_event_ref(e);
+ s->type = type;
+ s->pending_index = s->prepare_index = PRIOQ_IDX_NULL;
+
+ e->n_sources ++;
+
+ return s;
+}
+
+_public_ int sd_event_add_io(
+ sd_event *e,
+ int fd,
+ uint32_t events,
+ sd_event_io_handler_t callback,
+ void *userdata,
+ sd_event_source **ret) {
+
+ sd_event_source *s;
+ int r;
+
+ assert_return(e, -EINVAL);
+ assert_return(fd >= 0, -EINVAL);
+ assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ s = source_new(e, SOURCE_IO);
+ if (!s)
+ return -ENOMEM;
+
+ s->io.fd = fd;
+ s->io.events = events;
+ s->io.callback = callback;
+ s->userdata = userdata;
+ s->enabled = SD_EVENT_ON;
+
+ r = source_io_register(s, s->enabled, events);
+ if (r < 0) {
+ source_free(s);
+ return -errno;
+ }
+
+ *ret = s;
+ return 0;
+}
+
+static int event_setup_timer_fd(
+ sd_event *e,
+ EventSourceType type,
+ int *timer_fd,
+ clockid_t id) {
+
+ struct epoll_event ev = {};
+ int r, fd;
+ sd_id128_t bootid;
+
+ assert(e);
+ assert(timer_fd);
+
+ if (_likely_(*timer_fd >= 0))
+ return 0;
+
+ fd = timerfd_create(id, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ ev.events = EPOLLIN;
+ ev.data.ptr = INT_TO_PTR(type);
+
+ r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, fd, &ev);
+ if (r < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ /* When we sleep for longer, we try to realign the wakeup to
+ the same time wihtin each minute/second/250ms, so that
+ events all across the system can be coalesced into a single
+ CPU wakeup. However, let's take some system-specific
+ randomness for this value, so that in a network of systems
+ with synced clocks timer events are distributed a
+ bit. Here, we calculate a perturbation usec offset from the
+ boot ID. */
+
+ if (sd_id128_get_boot(&bootid) >= 0)
+ e->perturb = (bootid.qwords[0] ^ bootid.qwords[1]) % USEC_PER_MINUTE;
+
+ *timer_fd = fd;
+ return 0;
+}
+
+static int event_add_time_internal(
+ sd_event *e,
+ EventSourceType type,
+ int *timer_fd,
+ clockid_t id,
+ Prioq **earliest,
+ Prioq **latest,
+ uint64_t usec,
+ uint64_t accuracy,
+ sd_event_time_handler_t callback,
+ void *userdata,
+ sd_event_source **ret) {
+
+ sd_event_source *s;
+ int r;
+
+ assert_return(e, -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(usec != (uint64_t) -1, -EINVAL);
+ assert_return(accuracy != (uint64_t) -1, -EINVAL);
+ assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ assert(timer_fd);
+ assert(earliest);
+ assert(latest);
+
+ if (!*earliest) {
+ *earliest = prioq_new(earliest_time_prioq_compare);
+ if (!*earliest)
+ return -ENOMEM;
+ }
+
+ if (!*latest) {
+ *latest = prioq_new(latest_time_prioq_compare);
+ if (!*latest)
+ return -ENOMEM;
+ }
+
+ if (*timer_fd < 0) {
+ r = event_setup_timer_fd(e, type, timer_fd, id);
+ if (r < 0)
+ return r;
+ }
+
+ s = source_new(e, type);
+ if (!s)
+ return -ENOMEM;
+
+ s->time.next = usec;
+ s->time.accuracy = accuracy == 0 ? DEFAULT_ACCURACY_USEC : accuracy;
+ s->time.callback = callback;
+ s->time.earliest_index = s->time.latest_index = PRIOQ_IDX_NULL;
+ s->userdata = userdata;
+ s->enabled = SD_EVENT_ONESHOT;
+
+ r = prioq_put(*earliest, s, &s->time.earliest_index);
+ if (r < 0)
+ goto fail;
+
+ r = prioq_put(*latest, s, &s->time.latest_index);
+ if (r < 0)
+ goto fail;
+
+ *ret = s;
+ return 0;
+
+fail:
+ source_free(s);
+ return r;
+}
+
+_public_ int sd_event_add_monotonic(sd_event *e,
+ uint64_t usec,
+ uint64_t accuracy,
+ sd_event_time_handler_t callback,
+ void *userdata,
+ sd_event_source **ret) {
+
+ return event_add_time_internal(e, SOURCE_MONOTONIC, &e->monotonic_fd, CLOCK_MONOTONIC, &e->monotonic_earliest, &e->monotonic_latest, usec, accuracy, callback, userdata, ret);
+}
+
+_public_ int sd_event_add_realtime(sd_event *e,
+ uint64_t usec,
+ uint64_t accuracy,
+ sd_event_time_handler_t callback,
+ void *userdata,
+ sd_event_source **ret) {
+
+ return event_add_time_internal(e, SOURCE_REALTIME, &e->realtime_fd, CLOCK_REALTIME, &e->realtime_earliest, &e->monotonic_latest, usec, accuracy, callback, userdata, ret);
+}
+
+static int event_update_signal_fd(sd_event *e) {
+ struct epoll_event ev = {};
+ bool add_to_epoll;
+ int r;
+
+ assert(e);
+
+ add_to_epoll = e->signal_fd < 0;
+
+ r = signalfd(e->signal_fd, &e->sigset, SFD_NONBLOCK|SFD_CLOEXEC);
+ if (r < 0)
+ return -errno;
+
+ e->signal_fd = r;
+
+ if (!add_to_epoll)
+ return 0;
+
+ ev.events = EPOLLIN;
+ ev.data.ptr = INT_TO_PTR(SOURCE_SIGNAL);
+
+ r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->signal_fd, &ev);
+ if (r < 0) {
+ close_nointr_nofail(e->signal_fd);
+ e->signal_fd = -1;
+
+ return -errno;
+ }
+
+ return 0;
+}
+
+_public_ int sd_event_add_signal(
+ sd_event *e,
+ int sig,
+ sd_event_signal_handler_t callback,
+ void *userdata,
+ sd_event_source **ret) {
+
+ sd_event_source *s;
+ sigset_t ss;
+ int r;
+
+ assert_return(e, -EINVAL);
+ assert_return(sig > 0, -EINVAL);
+ assert_return(sig < _NSIG, -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ r = pthread_sigmask(SIG_SETMASK, NULL, &ss);
+ if (r < 0)
+ return -errno;
+
+ if (!sigismember(&ss, sig))
+ return -EBUSY;
+
+ if (!e->signal_sources) {
+ e->signal_sources = new0(sd_event_source*, _NSIG);
+ if (!e->signal_sources)
+ return -ENOMEM;
+ } else if (e->signal_sources[sig])
+ return -EBUSY;
+
+ s = source_new(e, SOURCE_SIGNAL);
+ if (!s)
+ return -ENOMEM;
+
+ s->signal.sig = sig;
+ s->signal.callback = callback;
+ s->userdata = userdata;
+ s->enabled = SD_EVENT_ON;
+
+ e->signal_sources[sig] = s;
+ assert_se(sigaddset(&e->sigset, sig) == 0);
+
+ if (sig != SIGCHLD || e->n_enabled_child_sources == 0) {
+ r = event_update_signal_fd(e);
+ if (r < 0) {
+ source_free(s);
+ return r;
+ }
+ }
+
+ *ret = s;
+ return 0;
+}
+
+_public_ int sd_event_add_child(
+ sd_event *e,
+ pid_t pid,
+ int options,
+ sd_event_child_handler_t callback,
+ void *userdata,
+ sd_event_source **ret) {
+
+ sd_event_source *s;
+ int r;
+
+ assert_return(e, -EINVAL);
+ assert_return(pid > 1, -EINVAL);
+ assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL);
+ assert_return(options != 0, -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ r = hashmap_ensure_allocated(&e->child_sources, trivial_hash_func, trivial_compare_func);
+ if (r < 0)
+ return r;
+
+ if (hashmap_contains(e->child_sources, INT_TO_PTR(pid)))
+ return -EBUSY;
+
+ s = source_new(e, SOURCE_CHILD);
+ if (!s)
+ return -ENOMEM;
+
+ s->child.pid = pid;
+ s->child.options = options;
+ s->child.callback = callback;
+ s->userdata = userdata;
+ s->enabled = SD_EVENT_ONESHOT;
+
+ r = hashmap_put(e->child_sources, INT_TO_PTR(pid), s);
+ if (r < 0) {
+ source_free(s);
+ return r;
+ }
+
+ e->n_enabled_child_sources ++;
+
+ assert_se(sigaddset(&e->sigset, SIGCHLD) == 0);
+
+ if (!e->signal_sources || !e->signal_sources[SIGCHLD]) {
+ r = event_update_signal_fd(e);
+ if (r < 0) {
+ source_free(s);
+ return -errno;
+ }
+ }
+
+ e->need_process_child = true;
+
+ *ret = s;
+ return 0;
+}
+
+_public_ int sd_event_add_defer(
+ sd_event *e,
+ sd_event_handler_t callback,
+ void *userdata,
+ sd_event_source **ret) {
+
+ sd_event_source *s;
+ int r;
+
+ assert_return(e, -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ s = source_new(e, SOURCE_DEFER);
+ if (!s)
+ return -ENOMEM;
+
+ s->defer.callback = callback;
+ s->userdata = userdata;
+ s->enabled = SD_EVENT_ONESHOT;
+
+ r = source_set_pending(s, true);
+ if (r < 0) {
+ source_free(s);
+ return r;
+ }
+
+ *ret = s;
+ return 0;
+}
+
+_public_ int sd_event_add_exit(
+ sd_event *e,
+ sd_event_handler_t callback,
+ void *userdata,
+ sd_event_source **ret) {
+
+ sd_event_source *s;
+ int r;
+
+ assert_return(e, -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ if (!e->exit) {
+ e->exit = prioq_new(exit_prioq_compare);
+ if (!e->exit)
+ return -ENOMEM;
+ }
+
+ s = source_new(e, SOURCE_EXIT);
+ if (!s)
+ return -ENOMEM;
+
+ s->exit.callback = callback;
+ s->userdata = userdata;
+ s->exit.prioq_index = PRIOQ_IDX_NULL;
+ s->enabled = SD_EVENT_ONESHOT;
+
+ r = prioq_put(s->event->exit, s, &s->exit.prioq_index);
+ if (r < 0) {
+ source_free(s);
+ return r;
+ }
+
+ *ret = s;
+ return 0;
+}
+
+_public_ sd_event_source* sd_event_source_ref(sd_event_source *s) {
+ assert_return(s, NULL);
+
+ assert(s->n_ref >= 1);
+ s->n_ref++;
+
+ return s;
+}
+
+_public_ sd_event_source* sd_event_source_unref(sd_event_source *s) {
+
+ if (!s)
+ return NULL;
+
+ assert(s->n_ref >= 1);
+ s->n_ref--;
+
+ if (s->n_ref <= 0) {
+ /* Here's a special hack: when we are called from a
+ * dispatch handler we won't free the event source
+ * immediately, but we will detach the fd from the
+ * epoll. This way it is safe for the caller to unref
+ * the event source and immediately close the fd, but
+ * we still retain a valid event source object after
+ * the callback. */
+
+ if (s->dispatching) {
+ if (s->type == SOURCE_IO)
+ source_io_unregister(s);
+ } else
+ source_free(s);
+ }
+
+ return NULL;
+}
+
+_public_ sd_event *sd_event_source_get_event(sd_event_source *s) {
+ assert_return(s, NULL);
+
+ return s->event;
+}
+
+_public_ int sd_event_source_get_pending(sd_event_source *s) {
+ assert_return(s, -EINVAL);
+ assert_return(s->type != SOURCE_EXIT, -EDOM);
+ assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ return s->pending;
+}
+
+_public_ int sd_event_source_get_io_fd(sd_event_source *s) {
+ assert_return(s, -EINVAL);
+ assert_return(s->type == SOURCE_IO, -EDOM);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ return s->io.fd;
+}
+
+_public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
+ int r;
+
+ assert_return(s, -EINVAL);
+ assert_return(fd >= 0, -EINVAL);
+ assert_return(s->type == SOURCE_IO, -EDOM);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ if (s->io.fd == fd)
+ return 0;
+
+ if (s->enabled == SD_EVENT_OFF) {
+ s->io.fd = fd;
+ s->io.registered = false;
+ } else {
+ int saved_fd;
+
+ saved_fd = s->io.fd;
+ assert(s->io.registered);
+
+ s->io.fd = fd;
+ s->io.registered = false;
+
+ r = source_io_register(s, s->enabled, s->io.events);
+ if (r < 0) {
+ s->io.fd = saved_fd;
+ s->io.registered = true;
+ return r;
+ }
+
+ epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL);
+ }
+
+ return 0;
+}
+
+_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) {
+ assert_return(s, -EINVAL);
+ assert_return(events, -EINVAL);
+ assert_return(s->type == SOURCE_IO, -EDOM);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ *events = s->io.events;
+ return 0;
+}
+
+_public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) {
+ int r;
+
+ assert_return(s, -EINVAL);
+ assert_return(s->type == SOURCE_IO, -EDOM);
+ assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
+ assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ if (s->io.events == events)
+ return 0;
+
+ if (s->enabled != SD_EVENT_OFF) {
+ r = source_io_register(s, s->enabled, events);
+ if (r < 0)
+ return r;
+ }
+
+ s->io.events = events;
+ source_set_pending(s, false);
+
+ return 0;
+}
+
+_public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents) {
+ assert_return(s, -EINVAL);
+ assert_return(revents, -EINVAL);
+ assert_return(s->type == SOURCE_IO, -EDOM);
+ assert_return(s->pending, -ENODATA);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ *revents = s->io.revents;
+ return 0;
+}
+
+_public_ int sd_event_source_get_signal(sd_event_source *s) {
+ assert_return(s, -EINVAL);
+ assert_return(s->type == SOURCE_SIGNAL, -EDOM);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ return s->signal.sig;
+}
+
+_public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority) {
+ assert_return(s, -EINVAL);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ return s->priority;
+}
+
+_public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) {
+ assert_return(s, -EINVAL);
+ assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ if (s->priority == priority)
+ return 0;
+
+ s->priority = priority;
+
+ if (s->pending)
+ prioq_reshuffle(s->event->pending, s, &s->pending_index);
+
+ if (s->prepare)
+ prioq_reshuffle(s->event->prepare, s, &s->prepare_index);
+
+ if (s->type == SOURCE_EXIT)
+ prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index);
+
+ return 0;
+}
+
+_public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) {
+ assert_return(s, -EINVAL);
+ assert_return(m, -EINVAL);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ *m = s->enabled;
+ return 0;
+}
+
+_public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
+ int r;
+
+ assert_return(s, -EINVAL);
+ assert_return(m == SD_EVENT_OFF || m == SD_EVENT_ON || m == SD_EVENT_ONESHOT, -EINVAL);
+ assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ if (s->enabled == m)
+ return 0;
+
+ if (m == SD_EVENT_OFF) {
+
+ switch (s->type) {
+
+ case SOURCE_IO:
+ r = source_io_unregister(s);
+ if (r < 0)
+ return r;
+
+ s->enabled = m;
+ break;
+
+ case SOURCE_MONOTONIC:
+ s->enabled = m;
+ prioq_reshuffle(s->event->monotonic_earliest, s, &s->time.earliest_index);
+ prioq_reshuffle(s->event->monotonic_latest, s, &s->time.latest_index);
+ break;
+
+ case SOURCE_REALTIME:
+ s->enabled = m;
+ prioq_reshuffle(s->event->realtime_earliest, s, &s->time.earliest_index);
+ prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index);
+ break;
+
+ case SOURCE_SIGNAL:
+ s->enabled = m;
+ if (s->signal.sig != SIGCHLD || s->event->n_enabled_child_sources == 0) {
+ assert_se(sigdelset(&s->event->sigset, s->signal.sig) == 0);
+ event_update_signal_fd(s->event);
+ }
+
+ break;
+
+ case SOURCE_CHILD:
+ s->enabled = m;
+
+ assert(s->event->n_enabled_child_sources > 0);
+ s->event->n_enabled_child_sources--;
+
+ if (!s->event->signal_sources || !s->event->signal_sources[SIGCHLD]) {
+ assert_se(sigdelset(&s->event->sigset, SIGCHLD) == 0);
+ event_update_signal_fd(s->event);
+ }
+
+ break;
+
+ case SOURCE_EXIT:
+ s->enabled = m;
+ prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index);
+ break;
+
+ case SOURCE_DEFER:
+ s->enabled = m;
+ break;
+
+ case SOURCE_WATCHDOG:
+ assert_not_reached("Wut? I shouldn't exist.");
+ }
+
+ } else {
+ switch (s->type) {
+
+ case SOURCE_IO:
+ r = source_io_register(s, m, s->io.events);
+ if (r < 0)
+ return r;
+
+ s->enabled = m;
+ break;
+
+ case SOURCE_MONOTONIC:
+ s->enabled = m;
+ prioq_reshuffle(s->event->monotonic_earliest, s, &s->time.earliest_index);
+ prioq_reshuffle(s->event->monotonic_latest, s, &s->time.latest_index);
+ break;
+
+ case SOURCE_REALTIME:
+ s->enabled = m;
+ prioq_reshuffle(s->event->realtime_earliest, s, &s->time.earliest_index);
+ prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index);
+ break;
+
+ case SOURCE_SIGNAL:
+ s->enabled = m;
+
+ if (s->signal.sig != SIGCHLD || s->event->n_enabled_child_sources == 0) {
+ assert_se(sigaddset(&s->event->sigset, s->signal.sig) == 0);
+ event_update_signal_fd(s->event);
+ }
+ break;
+
+ case SOURCE_CHILD:
+ s->enabled = m;
+
+ if (s->enabled == SD_EVENT_OFF) {
+ s->event->n_enabled_child_sources++;
+
+ if (!s->event->signal_sources || !s->event->signal_sources[SIGCHLD]) {
+ assert_se(sigaddset(&s->event->sigset, SIGCHLD) == 0);
+ event_update_signal_fd(s->event);
+ }
+ }
+ break;
+
+ case SOURCE_EXIT:
+ s->enabled = m;
+ prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index);
+ break;
+
+ case SOURCE_DEFER:
+ s->enabled = m;
+ break;
+
+ case SOURCE_WATCHDOG:
+ assert_not_reached("Wut? I shouldn't exist.");
+ }
+ }
+
+ if (s->pending)
+ prioq_reshuffle(s->event->pending, s, &s->pending_index);
+
+ if (s->prepare)
+ prioq_reshuffle(s->event->prepare, s, &s->prepare_index);
+
+ return 0;
+}
+
+_public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) {
+ assert_return(s, -EINVAL);
+ assert_return(usec, -EINVAL);
+ assert_return(s->type == SOURCE_REALTIME || s->type == SOURCE_MONOTONIC, -EDOM);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ *usec = s->time.next;
+ return 0;
+}
+
+_public_ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) {
+ assert_return(s, -EINVAL);
+ assert_return(usec != (uint64_t) -1, -EINVAL);
+ assert_return(s->type == SOURCE_REALTIME || s->type == SOURCE_MONOTONIC, -EDOM);
+ assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ s->time.next = usec;
+
+ source_set_pending(s, false);
+
+ if (s->type == SOURCE_REALTIME) {
+ prioq_reshuffle(s->event->realtime_earliest, s, &s->time.earliest_index);
+ prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index);
+ } else {
+ prioq_reshuffle(s->event->monotonic_earliest, s, &s->time.earliest_index);
+ prioq_reshuffle(s->event->monotonic_latest, s, &s->time.latest_index);
+ }
+
+ return 0;
+}
+
+_public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) {
+ assert_return(s, -EINVAL);
+ assert_return(usec, -EINVAL);
+ assert_return(s->type == SOURCE_REALTIME || s->type == SOURCE_MONOTONIC, -EDOM);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ *usec = s->time.accuracy;
+ return 0;
+}
+
+_public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec) {
+ assert_return(s, -EINVAL);
+ assert_return(usec != (uint64_t) -1, -EINVAL);
+ assert_return(s->type == SOURCE_REALTIME || s->type == SOURCE_MONOTONIC, -EDOM);
+ assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ if (usec == 0)
+ usec = DEFAULT_ACCURACY_USEC;
+
+ s->time.accuracy = usec;
+
+ source_set_pending(s, false);
+
+ if (s->type == SOURCE_REALTIME)
+ prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index);
+ else
+ prioq_reshuffle(s->event->monotonic_latest, s, &s->time.latest_index);
+
+ return 0;
+}
+
+_public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) {
+ assert_return(s, -EINVAL);
+ assert_return(pid, -EINVAL);
+ assert_return(s->type == SOURCE_CHILD, -EDOM);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ *pid = s->child.pid;
+ return 0;
+}
+
+_public_ int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback) {
+ int r;
+
+ assert_return(s, -EINVAL);
+ assert_return(s->type != SOURCE_EXIT, -EDOM);
+ assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(s->event), -ECHILD);
+
+ if (s->prepare == callback)
+ return 0;
+
+ if (callback && s->prepare) {
+ s->prepare = callback;
+ return 0;
+ }
+
+ r = prioq_ensure_allocated(&s->event->prepare, prepare_prioq_compare);
+ if (r < 0)
+ return r;
+
+ s->prepare = callback;
+
+ if (callback) {
+ r = prioq_put(s->event->prepare, s, &s->prepare_index);
+ if (r < 0)
+ return r;
+ } else
+ prioq_remove(s->event->prepare, s, &s->prepare_index);
+
+ return 0;
+}
+
+_public_ void* sd_event_source_get_userdata(sd_event_source *s) {
+ assert_return(s, NULL);
+
+ return s->userdata;
+}
+
+_public_ void *sd_event_source_set_userdata(sd_event_source *s, void *userdata) {
+ void *ret;
+
+ assert_return(s, NULL);
+
+ ret = s->userdata;
+ s->userdata = userdata;
+
+ return ret;
+}
+
+static usec_t sleep_between(sd_event *e, usec_t a, usec_t b) {
+ usec_t c;
+ assert(e);
+ assert(a <= b);
+
+ if (a <= 0)
+ return 0;
+
+ if (b <= a + 1)
+ return a;
+
+ /*
+ Find a good time to wake up again between times a and b. We
+ have two goals here:
+
+ a) We want to wake up as seldom as possible, hence prefer
+ later times over earlier times.
+
+ b) But if we have to wake up, then let's make sure to
+ dispatch as much as possible on the entire system.
+
+ We implement this by waking up everywhere at the same time
+ within any given minute if we can, synchronised via the
+ perturbation value determined from the boot ID. If we can't,
+ then we try to find the same spot in every 10s, then 1s and
+ then 250ms step. Otherwise, we pick the last possible time
+ to wake up.
+ */
+
+ c = (b / USEC_PER_MINUTE) * USEC_PER_MINUTE + e->perturb;
+ if (c >= b) {
+ if (_unlikely_(c < USEC_PER_MINUTE))
+ return b;
+
+ c -= USEC_PER_MINUTE;
+ }
+
+ if (c >= a)
+ return c;
+
+ c = (b / (USEC_PER_SEC*10)) * (USEC_PER_SEC*10) + (e->perturb % (USEC_PER_SEC*10));
+ if (c >= b) {
+ if (_unlikely_(c < USEC_PER_SEC*10))
+ return b;
+
+ c -= USEC_PER_SEC*10;
+ }
+
+ if (c >= a)
+ return c;
+
+ c = (b / USEC_PER_SEC) * USEC_PER_SEC + (e->perturb % USEC_PER_SEC);
+ if (c >= b) {
+ if (_unlikely_(c < USEC_PER_SEC))
+ return b;
+
+ c -= USEC_PER_SEC;
+ }
+
+ if (c >= a)
+ return c;
+
+ c = (b / (USEC_PER_MSEC*250)) * (USEC_PER_MSEC*250) + (e->perturb % (USEC_PER_MSEC*250));
+ if (c >= b) {
+ if (_unlikely_(c < USEC_PER_MSEC*250))
+ return b;
+
+ c -= USEC_PER_MSEC*250;
+ }
+
+ if (c >= a)
+ return c;
+
+ return b;
+}
+
+static int event_arm_timer(
+ sd_event *e,
+ int timer_fd,
+ Prioq *earliest,
+ Prioq *latest,
+ usec_t *next) {
+
+ struct itimerspec its = {};
+ sd_event_source *a, *b;
+ usec_t t;
+ int r;
+
+ assert(e);
+ assert(next);
+
+ a = prioq_peek(earliest);
+ if (!a || a->enabled == SD_EVENT_OFF) {
+
+ if (timer_fd < 0)
+ return 0;
+
+ if (*next == (usec_t) -1)
+ return 0;
+
+ /* disarm */
+ r = timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &its, NULL);
+ if (r < 0)
+ return r;
+
+ *next = (usec_t) -1;
+
+ return 0;
+ }
+
+ b = prioq_peek(latest);
+ assert_se(b && b->enabled != SD_EVENT_OFF);
+
+ t = sleep_between(e, a->time.next, b->time.next + b->time.accuracy);
+ if (*next == t)
+ return 0;
+
+ assert_se(timer_fd >= 0);
+
+ if (t == 0) {
+ /* We don' want to disarm here, just mean some time looooong ago. */
+ its.it_value.tv_sec = 0;
+ its.it_value.tv_nsec = 1;
+ } else
+ timespec_store(&its.it_value, t);
+
+ r = timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &its, NULL);
+ if (r < 0)
+ return -errno;
+
+ *next = t;
+ return 0;
+}
+
+static int process_io(sd_event *e, sd_event_source *s, uint32_t revents) {
+ assert(e);
+ assert(s);
+ assert(s->type == SOURCE_IO);
+
+ /* If the event source was already pending, we just OR in the
+ * new revents, otherwise we reset the value. The ORing is
+ * necessary to handle EPOLLONESHOT events properly where
+ * readability might happen independently of writability, and
+ * we need to keep track of both */
+
+ if (s->pending)
+ s->io.revents |= revents;
+ else
+ s->io.revents = revents;
+
+ return source_set_pending(s, true);
+}
+
+static int flush_timer(sd_event *e, int fd, uint32_t events, usec_t *next) {
+ uint64_t x;
+ ssize_t ss;
+
+ assert(e);
+ assert(fd >= 0);
+
+ assert_return(events == EPOLLIN, -EIO);
+
+ ss = read(fd, &x, sizeof(x));
+ if (ss < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return -errno;
+ }
+
+ if (_unlikely_(ss != sizeof(x)))
+ return -EIO;
+
+ if (next)
+ *next = (usec_t) -1;
+
+ return 0;
+}
+
+static int process_timer(
+ sd_event *e,
+ usec_t n,
+ Prioq *earliest,
+ Prioq *latest) {
+
+ sd_event_source *s;
+ int r;
+
+ assert(e);
+
+ for (;;) {
+ s = prioq_peek(earliest);
+ if (!s ||
+ s->time.next > n ||
+ s->enabled == SD_EVENT_OFF ||
+ s->pending)
+ break;
+
+ r = source_set_pending(s, true);
+ if (r < 0)
+ return r;
+
+ prioq_reshuffle(earliest, s, &s->time.earliest_index);
+ prioq_reshuffle(latest, s, &s->time.latest_index);
+ }
+
+ return 0;
+}
+
+static int process_child(sd_event *e) {
+ sd_event_source *s;
+ Iterator i;
+ int r;
+
+ assert(e);
+
+ e->need_process_child = false;
+
+ /*
+ So, this is ugly. We iteratively invoke waitid() with P_PID
+ + WNOHANG for each PID we wait for, instead of using
+ P_ALL. This is because we only want to get child
+ information of very specific child processes, and not all
+ of them. We might not have processed the SIGCHLD even of a
+ previous invocation and we don't want to maintain a
+ unbounded *per-child* event queue, hence we really don't
+ want anything flushed out of the kernel's queue that we
+ don't care about. Since this is O(n) this means that if you
+ have a lot of processes you probably want to handle SIGCHLD
+ yourself.
+
+ We do not reap the children here (by using WNOWAIT), this
+ is only done after the event source is dispatched so that
+ the callback still sees the process as a zombie.
+ */
+
+ HASHMAP_FOREACH(s, e->child_sources, i) {
+ assert(s->type == SOURCE_CHILD);
+
+ if (s->pending)
+ continue;
+
+ if (s->enabled == SD_EVENT_OFF)
+ continue;
+
+ zero(s->child.siginfo);
+ r = waitid(P_PID, s->child.pid, &s->child.siginfo,
+ WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options);
+ if (r < 0)
+ return -errno;
+
+ if (s->child.siginfo.si_pid != 0) {
+ bool zombie =
+ s->child.siginfo.si_code == CLD_EXITED ||
+ s->child.siginfo.si_code == CLD_KILLED ||
+ s->child.siginfo.si_code == CLD_DUMPED;
+
+ if (!zombie && (s->child.options & WEXITED)) {
+ /* If the child isn't dead then let's
+ * immediately remove the state change
+ * from the queue, since there's no
+ * benefit in leaving it queued */
+
+ assert(s->child.options & (WSTOPPED|WCONTINUED));
+ waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
+ }
+
+ r = source_set_pending(s, true);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int process_signal(sd_event *e, uint32_t events) {
+ bool read_one = false;
+ int r;
+
+ assert(e);
+ assert(e->signal_sources);
+
+ assert_return(events == EPOLLIN, -EIO);
+
+ for (;;) {
+ struct signalfd_siginfo si;
+ ssize_t ss;
+ sd_event_source *s;
+
+ ss = read(e->signal_fd, &si, sizeof(si));
+ if (ss < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return read_one;
+
+ return -errno;
+ }
+
+ if (_unlikely_(ss != sizeof(si)))
+ return -EIO;
+
+ read_one = true;
+
+ s = e->signal_sources[si.ssi_signo];
+ if (si.ssi_signo == SIGCHLD) {
+ r = process_child(e);
+ if (r < 0)
+ return r;
+ if (r > 0 || !s)
+ continue;
+ } else
+ if (!s)
+ return -EIO;
+
+ s->signal.siginfo = si;
+ r = source_set_pending(s, true);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int source_dispatch(sd_event_source *s) {
+ int r = 0;
+
+ assert(s);
+ assert(s->pending || s->type == SOURCE_EXIT);
+
+ if (s->type != SOURCE_DEFER && s->type != SOURCE_EXIT) {
+ r = source_set_pending(s, false);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->enabled == SD_EVENT_ONESHOT) {
+ r = sd_event_source_set_enabled(s, SD_EVENT_OFF);
+ if (r < 0)
+ return r;
+ }
+
+ s->dispatching = true;
+
+ switch (s->type) {
+
+ case SOURCE_IO:
+ r = s->io.callback(s, s->io.fd, s->io.revents, s->userdata);
+ break;
+
+ case SOURCE_MONOTONIC:
+ r = s->time.callback(s, s->time.next, s->userdata);
+ break;
+
+ case SOURCE_REALTIME:
+ r = s->time.callback(s, s->time.next, s->userdata);
+ break;
+
+ case SOURCE_SIGNAL:
+ r = s->signal.callback(s, &s->signal.siginfo, s->userdata);
+ break;
+
+ case SOURCE_CHILD: {
+ bool zombie;
+
+ zombie = s->child.siginfo.si_code == CLD_EXITED ||
+ s->child.siginfo.si_code == CLD_KILLED ||
+ s->child.siginfo.si_code == CLD_DUMPED;
+
+ r = s->child.callback(s, &s->child.siginfo, s->userdata);
+
+ /* Now, reap the PID for good. */
+ if (zombie)
+ waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED);
+
+ break;
+ }
+
+ case SOURCE_DEFER:
+ r = s->defer.callback(s, s->userdata);
+ break;
+
+ case SOURCE_EXIT:
+ r = s->exit.callback(s, s->userdata);
+ break;
+
+ case SOURCE_WATCHDOG:
+ assert_not_reached("Wut? I shouldn't exist.");
+ }
+
+ s->dispatching = false;
+
+ if (r < 0)
+ log_debug("Event source %p returned error, disabling: %s", s, strerror(-r));
+
+ if (s->n_ref == 0)
+ source_free(s);
+ else if (r < 0)
+ sd_event_source_set_enabled(s, SD_EVENT_OFF);
+
+ return 1;
+}
+
+static int event_prepare(sd_event *e) {
+ int r;
+
+ assert(e);
+
+ for (;;) {
+ sd_event_source *s;
+
+ s = prioq_peek(e->prepare);
+ if (!s || s->prepare_iteration == e->iteration || s->enabled == SD_EVENT_OFF)
+ break;
+
+ s->prepare_iteration = e->iteration;
+ r = prioq_reshuffle(e->prepare, s, &s->prepare_index);
+ if (r < 0)
+ return r;
+
+ assert(s->prepare);
+
+ s->dispatching = true;
+ r = s->prepare(s, s->userdata);
+ s->dispatching = false;
+
+ if (r < 0)
+ log_debug("Prepare callback of event source %p returned error, disabling: %s", s, strerror(-r));
+
+ if (s->n_ref == 0)
+ source_free(s);
+ else if (r < 0)
+ sd_event_source_set_enabled(s, SD_EVENT_OFF);
+ }
+
+ return 0;
+}
+
+static int dispatch_exit(sd_event *e) {
+ sd_event_source *p;
+ int r;
+
+ assert(e);
+
+ p = prioq_peek(e->exit);
+ if (!p || p->enabled == SD_EVENT_OFF) {
+ e->state = SD_EVENT_FINISHED;
+ return 0;
+ }
+
+ sd_event_ref(e);
+ e->iteration++;
+ e->state = SD_EVENT_EXITING;
+
+ r = source_dispatch(p);
+
+ e->state = SD_EVENT_PASSIVE;
+ sd_event_unref(e);
+
+ return r;
+}
+
+static sd_event_source* event_next_pending(sd_event *e) {
+ sd_event_source *p;
+
+ assert(e);
+
+ p = prioq_peek(e->pending);
+ if (!p)
+ return NULL;
+
+ if (p->enabled == SD_EVENT_OFF)
+ return NULL;
+
+ return p;
+}
+
+static int arm_watchdog(sd_event *e) {
+ struct itimerspec its = {};
+ usec_t t;
+ int r;
+
+ assert(e);
+ assert(e->watchdog_fd >= 0);
+
+ t = sleep_between(e,
+ e->watchdog_last + (e->watchdog_period / 2),
+ e->watchdog_last + (e->watchdog_period * 3 / 4));
+
+ timespec_store(&its.it_value, t);
+
+ r = timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int process_watchdog(sd_event *e) {
+ assert(e);
+
+ if (!e->watchdog)
+ return 0;
+
+ /* Don't notify watchdog too often */
+ if (e->watchdog_last + e->watchdog_period / 4 > e->timestamp.monotonic)
+ return 0;
+
+ sd_notify(false, "WATCHDOG=1");
+ e->watchdog_last = e->timestamp.monotonic;
+
+ return arm_watchdog(e);
+}
+
+_public_ int sd_event_run(sd_event *e, uint64_t timeout) {
+ struct epoll_event *ev_queue;
+ unsigned ev_queue_max;
+ sd_event_source *p;
+ int r, i, m;
+
+ assert_return(e, -EINVAL);
+ assert_return(!event_pid_changed(e), -ECHILD);
+ assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY);
+
+ if (e->exit_requested)
+ return dispatch_exit(e);
+
+ sd_event_ref(e);
+ e->iteration++;
+ e->state = SD_EVENT_RUNNING;
+
+ r = event_prepare(e);
+ if (r < 0)
+ goto finish;
+
+ r = event_arm_timer(e, e->monotonic_fd, e->monotonic_earliest, e->monotonic_latest, &e->monotonic_next);
+ if (r < 0)
+ goto finish;
+
+ r = event_arm_timer(e, e->realtime_fd, e->realtime_earliest, e->realtime_latest, &e->realtime_next);
+ if (r < 0)
+ goto finish;
+
+ if (event_next_pending(e) || e->need_process_child)
+ timeout = 0;
+ ev_queue_max = CLAMP(e->n_sources, 1U, EPOLL_QUEUE_MAX);
+ ev_queue = newa(struct epoll_event, ev_queue_max);
+
+ m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max,
+ timeout == (uint64_t) -1 ? -1 : (int) ((timeout + USEC_PER_MSEC - 1) / USEC_PER_MSEC));
+ if (m < 0) {
+ r = errno == EAGAIN || errno == EINTR ? 1 : -errno;
+ goto finish;
+ }
+
+ dual_timestamp_get(&e->timestamp);
+
+ for (i = 0; i < m; i++) {
+
+ if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_MONOTONIC))
+ r = flush_timer(e, e->monotonic_fd, ev_queue[i].events, &e->monotonic_next);
+ else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_REALTIME))
+ r = flush_timer(e, e->realtime_fd, ev_queue[i].events, &e->realtime_next);
+ else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_SIGNAL))
+ r = process_signal(e, ev_queue[i].events);
+ else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG))
+ r = flush_timer(e, e->watchdog_fd, ev_queue[i].events, NULL);
+ else
+ r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events);
+
+ if (r < 0)
+ goto finish;
+ }
+
+ r = process_watchdog(e);
+ if (r < 0)
+ goto finish;
+
+ r = process_timer(e, e->timestamp.monotonic, e->monotonic_earliest, e->monotonic_latest);
+ if (r < 0)
+ goto finish;
+
+ r = process_timer(e, e->timestamp.realtime, e->realtime_earliest, e->realtime_latest);
+ if (r < 0)
+ goto finish;
+
+ if (e->need_process_child) {
+ r = process_child(e);
+ if (r < 0)
+ goto finish;
+ }
+
+ p = event_next_pending(e);
+ if (!p) {
+ r = 1;
+ goto finish;
+ }
+
+ r = source_dispatch(p);
+
+finish:
+ e->state = SD_EVENT_PASSIVE;
+ sd_event_unref(e);
+
+ return r;
+}
+
+_public_ int sd_event_loop(sd_event *e) {
+ int r;
+
+ assert_return(e, -EINVAL);
+ assert_return(!event_pid_changed(e), -ECHILD);
+ assert_return(e->state == SD_EVENT_PASSIVE, -EBUSY);
+
+ sd_event_ref(e);
+
+ while (e->state != SD_EVENT_FINISHED) {
+ r = sd_event_run(e, (uint64_t) -1);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = e->exit_code;
+
+finish:
+ sd_event_unref(e);
+ return r;
+}
+
+_public_ int sd_event_get_state(sd_event *e) {
+ assert_return(e, -EINVAL);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ return e->state;
+}
+
+_public_ int sd_event_get_exit_code(sd_event *e, int *code) {
+ assert_return(e, -EINVAL);
+ assert_return(code, -EINVAL);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ if (!e->exit_requested)
+ return -ENODATA;
+
+ *code = e->exit_code;
+ return 0;
+}
+
+_public_ int sd_event_exit(sd_event *e, int code) {
+ assert_return(e, -EINVAL);
+ assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ e->exit_requested = true;
+ e->exit_code = code;
+
+ return 0;
+}
+
+_public_ int sd_event_get_now_realtime(sd_event *e, uint64_t *usec) {
+ assert_return(e, -EINVAL);
+ assert_return(usec, -EINVAL);
+ assert_return(dual_timestamp_is_set(&e->timestamp), -ENODATA);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ *usec = e->timestamp.realtime;
+ return 0;
+}
+
+_public_ int sd_event_get_now_monotonic(sd_event *e, uint64_t *usec) {
+ assert_return(e, -EINVAL);
+ assert_return(usec, -EINVAL);
+ assert_return(dual_timestamp_is_set(&e->timestamp), -ENODATA);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ *usec = e->timestamp.monotonic;
+ return 0;
+}
+
+_public_ int sd_event_default(sd_event **ret) {
+
+ static thread_local sd_event *default_event = NULL;
+ sd_event *e;
+ int r;
+
+ if (!ret)
+ return !!default_event;
+
+ if (default_event) {
+ *ret = sd_event_ref(default_event);
+ return 0;
+ }
+
+ r = sd_event_new(&e);
+ if (r < 0)
+ return r;
+
+ e->default_event_ptr = &default_event;
+ e->tid = gettid();
+ default_event = e;
+
+ *ret = e;
+ return 1;
+}
+
+_public_ int sd_event_get_tid(sd_event *e, pid_t *tid) {
+ assert_return(e, -EINVAL);
+ assert_return(tid, -EINVAL);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ if (e->tid != 0) {
+ *tid = e->tid;
+ return 0;
+ }
+
+ return -ENXIO;
+}
+
+_public_ int sd_event_set_watchdog(sd_event *e, int b) {
+ int r;
+
+ assert_return(e, -EINVAL);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ if (e->watchdog == !!b)
+ return e->watchdog;
+
+ if (b) {
+ struct epoll_event ev = {};
+
+ r = sd_watchdog_enabled(false, &e->watchdog_period);
+ if (r <= 0)
+ return r;
+
+ /* Issue first ping immediately */
+ sd_notify(false, "WATCHDOG=1");
+ e->watchdog_last = now(CLOCK_MONOTONIC);
+
+ e->watchdog_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (e->watchdog_fd < 0)
+ return -errno;
+
+ r = arm_watchdog(e);
+ if (r < 0)
+ goto fail;
+
+ ev.events = EPOLLIN;
+ ev.data.ptr = INT_TO_PTR(SOURCE_WATCHDOG);
+
+ r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->watchdog_fd, &ev);
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ } else {
+ if (e->watchdog_fd >= 0) {
+ epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, e->watchdog_fd, NULL);
+ close_nointr_nofail(e->watchdog_fd);
+ e->watchdog_fd = -1;
+ }
+ }
+
+ e->watchdog = !!b;
+ return e->watchdog;
+
+fail:
+ close_nointr_nofail(e->watchdog_fd);
+ e->watchdog_fd = -1;
+ return r;
+}
+
+_public_ int sd_event_get_watchdog(sd_event *e) {
+ assert_return(e, -EINVAL);
+ assert_return(!event_pid_changed(e), -ECHILD);
+
+ return e->watchdog;
+}
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
new file mode 100644
index 0000000..28ef6a3
--- /dev/null
+++ b/src/libsystemd/sd-event/test-event.c
@@ -0,0 +1,248 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-event.h"
+#include "log.h"
+#include "util.h"
+
+static int prepare_handler(sd_event_source *s, void *userdata) {
+ log_info("preparing %c", PTR_TO_INT(userdata));
+ return 1;
+}
+
+static bool got_a, got_b, got_c, got_unref;
+static unsigned got_d;
+
+static int unref_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ sd_event_source_unref(s);
+ got_unref = true;
+ return 0;
+}
+
+static int io_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+
+ log_info("got IO on %c", PTR_TO_INT(userdata));
+
+ if (userdata == INT_TO_PTR('a')) {
+ assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
+ assert_se(!got_a);
+ got_a = true;
+ } else if (userdata == INT_TO_PTR('b')) {
+ assert_se(!got_b);
+ got_b = true;
+ } else if (userdata == INT_TO_PTR('d')) {
+ got_d++;
+ if (got_d < 2)
+ assert_se(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT) >= 0);
+ else
+ assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
+ } else
+ assert_not_reached("Yuck!");
+
+ return 1;
+}
+
+static int child_handler(sd_event_source *s, const siginfo_t *si, void *userdata) {
+
+ assert(s);
+ assert(si);
+
+ log_info("got child on %c", PTR_TO_INT(userdata));
+
+ assert(userdata == INT_TO_PTR('f'));
+
+ assert_se(sd_event_exit(sd_event_source_get_event(s), 0) >= 0);
+ sd_event_source_unref(s);
+
+ return 1;
+}
+
+static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ sd_event_source *p;
+ sigset_t ss;
+ pid_t pid;
+
+ assert(s);
+ assert(si);
+
+ log_info("got signal on %c", PTR_TO_INT(userdata));
+
+ assert(userdata == INT_TO_PTR('e'));
+
+ assert_se(sigemptyset(&ss) >= 0);
+ assert_se(sigaddset(&ss, SIGCHLD) >= 0);
+ assert_se(sigprocmask(SIG_BLOCK, &ss, NULL) >= 0);
+
+ pid = fork();
+ assert_se(pid >= 0);
+
+ if (pid == 0)
+ _exit(0);
+
+ assert_se(sd_event_add_child(sd_event_source_get_event(s), pid, WEXITED, child_handler, INT_TO_PTR('f'), &p) >= 0);
+ assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
+
+ sd_event_source_unref(s);
+
+ return 1;
+}
+
+static int defer_handler(sd_event_source *s, void *userdata) {
+ sd_event_source *p;
+ sigset_t ss;
+
+ assert(s);
+
+ log_info("got defer on %c", PTR_TO_INT(userdata));
+
+ assert(userdata == INT_TO_PTR('d'));
+
+ assert_se(sigemptyset(&ss) >= 0);
+ assert_se(sigaddset(&ss, SIGUSR1) >= 0);
+ assert_se(sigprocmask(SIG_BLOCK, &ss, NULL) >= 0);
+ assert_se(sd_event_add_signal(sd_event_source_get_event(s), SIGUSR1, signal_handler, INT_TO_PTR('e'), &p) >= 0);
+ assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
+ raise(SIGUSR1);
+
+ sd_event_source_unref(s);
+
+ return 1;
+}
+
+static bool do_quit = false;
+
+static int time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
+ log_info("got timer on %c", PTR_TO_INT(userdata));
+
+ if (userdata == INT_TO_PTR('c')) {
+
+ if (do_quit) {
+ sd_event_source *p;
+
+ assert_se(sd_event_add_defer(sd_event_source_get_event(s), defer_handler, INT_TO_PTR('d'), &p) >= 0);
+ assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
+ } else {
+ assert(!got_c);
+ got_c = true;
+ }
+ } else
+ assert_not_reached("Huh?");
+
+ return 2;
+}
+
+static bool got_exit = false;
+
+static int exit_handler(sd_event_source *s, void *userdata) {
+ log_info("got quit handler on %c", PTR_TO_INT(userdata));
+
+ got_exit = true;
+
+ return 3;
+}
+
+int main(int argc, char *argv[]) {
+ sd_event *e = NULL;
+ sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL;
+ static const char ch = 'x';
+ int a[2] = { -1, -1 }, b[2] = { -1, -1}, d[2] = { -1, -1}, k[2] = { -1, -1 };
+
+ assert_se(pipe(a) >= 0);
+ assert_se(pipe(b) >= 0);
+ assert_se(pipe(d) >= 0);
+ assert_se(pipe(k) >= 0);
+
+ assert_se(sd_event_default(&e) >= 0);
+
+ assert_se(sd_event_set_watchdog(e, true) >= 0);
+
+ /* Test whether we cleanly can destroy an io event source from its own handler */
+ got_unref = false;
+ assert_se(sd_event_add_io(e, k[0], EPOLLIN, unref_handler, NULL, &t) >= 0);
+ assert_se(write(k[1], &ch, 1) == 1);
+ assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+ assert_se(got_unref);
+
+ got_a = false, got_b = false, got_c = false, got_d = 0;
+
+ /* Add a oneshot handler, trigger it, re-enable it, and trigger
+ * it again. */
+ assert_se(sd_event_add_io(e, d[0], EPOLLIN, io_handler, INT_TO_PTR('d'), &w) >= 0);
+ assert_se(sd_event_source_set_enabled(w, SD_EVENT_ONESHOT) >= 0);
+ assert_se(write(d[1], &ch, 1) >= 0);
+ assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+ assert_se(got_d == 1);
+ assert_se(write(d[1], &ch, 1) >= 0);
+ assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+ assert_se(got_d == 2);
+
+ assert_se(sd_event_add_io(e, a[0], EPOLLIN, io_handler, INT_TO_PTR('a'), &x) >= 0);
+ assert_se(sd_event_add_io(e, b[0], EPOLLIN, io_handler, INT_TO_PTR('b'), &y) >= 0);
+ assert_se(sd_event_add_monotonic(e, 0, 0, time_handler, INT_TO_PTR('c'), &z) >= 0);
+ assert_se(sd_event_add_exit(e, exit_handler, INT_TO_PTR('g'), &q) >= 0);
+
+ assert_se(sd_event_source_set_priority(x, 99) >= 0);
+ assert_se(sd_event_source_set_enabled(y, SD_EVENT_ONESHOT) >= 0);
+ assert_se(sd_event_source_set_prepare(x, prepare_handler) >= 0);
+ assert_se(sd_event_source_set_priority(z, 50) >= 0);
+ assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0);
+ assert_se(sd_event_source_set_prepare(z, prepare_handler) >= 0);
+
+ assert_se(write(a[1], &ch, 1) >= 0);
+ assert_se(write(b[1], &ch, 1) >= 0);
+
+ assert_se(!got_a && !got_b && !got_c);
+
+ assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+
+ assert_se(!got_a && got_b && !got_c);
+
+ assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+
+ assert_se(!got_a && got_b && got_c);
+
+ assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+
+ assert_se(got_a && got_b && got_c);
+
+ sd_event_source_unref(x);
+ sd_event_source_unref(y);
+
+ do_quit = true;
+ assert_se(sd_event_source_set_time(z, now(CLOCK_MONOTONIC) + 200 * USEC_PER_MSEC) >= 0);
+ assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0);
+
+ assert_se(sd_event_loop(e) >= 0);
+
+ sd_event_source_unref(z);
+ sd_event_source_unref(q);
+
+ sd_event_source_unref(w);
+
+ sd_event_unref(e);
+
+ close_pipe(a);
+ close_pipe(b);
+ close_pipe(d);
+ close_pipe(k);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-memfd.c b/src/libsystemd/sd-memfd.c
deleted file mode 100644
index 112c95f..0000000
--- a/src/libsystemd/sd-memfd.c
+++ /dev/null
@@ -1,220 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdio.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-
-#include "util.h"
-#include "kdbus.h"
-
-#include "sd-memfd.h"
-
-struct sd_memfd {
- int fd;
- FILE *f;
-};
-
-_public_ int sd_memfd_new(sd_memfd **m) {
- struct kdbus_cmd_memfd_make cmd = {
- .size = sizeof(struct kdbus_cmd_memfd_make),
- };
- _cleanup_close_ int kdbus = -1;
- sd_memfd *n;
-
- assert_return(m, -EINVAL);
-
- kdbus = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (kdbus < 0)
- return -errno;
-
- if (ioctl(kdbus, KDBUS_CMD_MEMFD_NEW, &cmd) < 0)
- return -errno;
-
- n = new0(struct sd_memfd, 1);
- if (!n) {
- close_nointr_nofail(cmd.fd);
- return -ENOMEM;
- }
-
- n->fd = cmd.fd;
- *m = n;
- return 0;
-}
-
-_public_ int sd_memfd_make(int fd, sd_memfd **m) {
- sd_memfd *n;
- uint64_t sz;
-
- assert_return(m, -EINVAL);
- assert_return(fd >= 0, -EINVAL);
-
- /* Check if this is a valid memfd */
- if (ioctl(fd, KDBUS_CMD_MEMFD_SIZE_GET, &sz) < 0)
- return -ENOTTY;
-
- n = new0(struct sd_memfd, 1);
- if (!n)
- return -ENOMEM;
-
- n->fd = fd;
- *m = n;
-
- return 0;
-}
-
-_public_ void sd_memfd_free(sd_memfd *m) {
- if (!m)
- return;
-
- if (m->f)
- fclose(m->f);
- else
- close_nointr_nofail(m->fd);
-
- free(m);
-}
-
-_public_ int sd_memfd_get_fd(sd_memfd *m) {
- assert_return(m, -EINVAL);
-
- return m->fd;
-}
-
-_public_ int sd_memfd_get_file(sd_memfd *m, FILE **f) {
- assert_return(m, -EINVAL);
- assert_return(f, -EINVAL);
-
- if (!m->f) {
- m->f = fdopen(m->fd, "r+");
- if (!m->f)
- return -errno;
- }
-
- *f = m->f;
- return 0;
-}
-
-_public_ int sd_memfd_dup_fd(sd_memfd *m) {
- int fd;
-
- assert_return(m, -EINVAL);
-
- fd = fcntl(m->fd, F_DUPFD_CLOEXEC, 3);
- if (fd < 0)
- return -errno;
-
- return fd;
-}
-
-_public_ int sd_memfd_map(sd_memfd *m, uint64_t offset, size_t size, void **p) {
- void *q;
- int sealed;
-
- assert_return(m, -EINVAL);
- assert_return(size > 0, -EINVAL);
- assert_return(p, -EINVAL);
-
- sealed = sd_memfd_get_sealed(m);
- if (sealed < 0)
- return sealed;
-
- q = mmap(NULL, size, sealed ? PROT_READ : PROT_READ|PROT_WRITE, MAP_SHARED, m->fd, offset);
- if (q == MAP_FAILED)
- return -errno;
-
- *p = q;
- return 0;
-}
-
-_public_ int sd_memfd_set_sealed(sd_memfd *m, int b) {
- int r;
-
- assert_return(m, -EINVAL);
-
- r = ioctl(m->fd, KDBUS_CMD_MEMFD_SEAL_SET, b);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-_public_ int sd_memfd_get_sealed(sd_memfd *m) {
- int r, b;
-
- assert_return(m, -EINVAL);
-
- r = ioctl(m->fd, KDBUS_CMD_MEMFD_SEAL_GET, &b);
- if (r < 0)
- return -errno;
-
- return !!b;
-}
-
-_public_ int sd_memfd_get_size(sd_memfd *m, uint64_t *sz) {
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(sz, -EINVAL);
-
- r = ioctl(m->fd, KDBUS_CMD_MEMFD_SIZE_GET, sz);
- if (r < 0)
- return -errno;
-
- return r;
-}
-
-_public_ int sd_memfd_set_size(sd_memfd *m, uint64_t sz) {
- int r;
-
- assert_return(m, -EINVAL);
-
- r = ioctl(m->fd, KDBUS_CMD_MEMFD_SIZE_SET, &sz);
- if (r < 0)
- return -errno;
-
- return r;
-}
-
-_public_ int sd_memfd_new_and_map(sd_memfd **m, size_t sz, void **p) {
- sd_memfd *n;
- int r;
-
- r = sd_memfd_new(&n);
- if (r < 0)
- return r;
-
- r = sd_memfd_set_size(n, sz);
- if (r < 0) {
- sd_memfd_free(n);
- return r;
- }
-
- r = sd_memfd_map(n, 0, sz, p);
- if (r < 0) {
- sd_memfd_free(n);
- return r;
- }
-
- *m = n;
- return 0;
-}
diff --git a/src/libsystemd/sd-resolve.c b/src/libsystemd/sd-resolve.c
deleted file mode 100644
index 464967b..0000000
--- a/src/libsystemd/sd-resolve.c
+++ /dev/null
@@ -1,1130 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2005-2008 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <unistd.h>
-#include <sys/select.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/wait.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-#include <dirent.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <stdint.h>
-#include <pthread.h>
-#include <sys/prctl.h>
-
-#include "sd-resolve.h"
-#include "util.h"
-
-#define MAX_WORKERS 16
-#define MAX_QUERIES 256
-#define BUFSIZE (10240)
-
-typedef enum {
- REQUEST_ADDRINFO,
- RESPONSE_ADDRINFO,
- REQUEST_NAMEINFO,
- RESPONSE_NAMEINFO,
- REQUEST_RES_QUERY,
- REQUEST_RES_SEARCH,
- RESPONSE_RES,
- REQUEST_TERMINATE,
- RESPONSE_DIED
-} QueryType;
-
-enum {
- REQUEST_RECV_FD = 0,
- REQUEST_SEND_FD = 1,
- RESPONSE_RECV_FD = 2,
- RESPONSE_SEND_FD = 3,
- MESSAGE_FD_MAX = 4
-};
-
-struct sd_resolve {
- int fds[MESSAGE_FD_MAX];
-
- pthread_t workers[MAX_WORKERS];
- unsigned valid_workers;
-
- unsigned current_id, current_index;
- sd_resolve_query* queries[MAX_QUERIES];
-
- sd_resolve_query *done_head, *done_tail;
-
- int n_queries;
- int dead;
-};
-
-struct sd_resolve_query {
- sd_resolve *resolve;
- int done;
- unsigned id;
- QueryType type;
- sd_resolve_query *done_next, *done_prev;
- int ret;
- int _errno;
- int _h_errno;
- struct addrinfo *addrinfo;
- char *serv, *host;
- void *userdata;
-};
-
-typedef struct RHeader {
- QueryType type;
- unsigned id;
- size_t length;
-} RHeader;
-
-typedef struct AddrInfoRequest {
- struct RHeader header;
- int hints_is_null;
- int ai_flags;
- int ai_family;
- int ai_socktype;
- int ai_protocol;
- size_t node_len, service_len;
-} AddrInfoRequest;
-
-typedef struct AddrInfoResponse {
- struct RHeader header;
- int ret;
- int _errno;
- int _h_errno;
- /* followed by addrinfo_serialization[] */
-} AddrInfoResponse;
-
-typedef struct AddrInfoSerialization {
- int ai_flags;
- int ai_family;
- int ai_socktype;
- int ai_protocol;
- size_t ai_addrlen;
- size_t canonname_len;
- /* Followed by ai_addr amd ai_canonname with variable lengths */
-} AddrInfoSerialization;
-
-typedef struct NameInfoRequest {
- struct RHeader header;
- int flags;
- socklen_t sockaddr_len;
- int gethost, getserv;
-} NameInfoRequest;
-
-typedef struct NameInfoResponse {
- struct RHeader header;
- size_t hostlen, servlen;
- int ret;
- int _errno;
- int _h_errno;
-} NameInfoResponse;
-
-typedef struct ResRequest {
- struct RHeader header;
- int class;
- int type;
- size_t dname_len;
-} ResRequest;
-
-typedef struct ResResponse {
- struct RHeader header;
- int ret;
- int _errno;
- int _h_errno;
-} ResResponse;
-
-typedef union Packet {
- RHeader rheader;
- AddrInfoRequest addrinfo_request;
- AddrInfoResponse addrinfo_response;
- NameInfoRequest nameinfo_request;
- NameInfoResponse nameinfo_response;
- ResRequest res_request;
- ResResponse res_response;
-} Packet;
-
-static int send_died(int out_fd) {
- RHeader rh = {};
- assert(out_fd > 0);
-
- rh.type = RESPONSE_DIED;
- rh.id = 0;
- rh.length = sizeof(rh);
-
- return send(out_fd, &rh, rh.length, MSG_NOSIGNAL);
-}
-
-static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *length, size_t maxlength) {
- AddrInfoSerialization s;
- size_t cnl, l;
- assert(p);
- assert(ai);
- assert(length);
- assert(*length <= maxlength);
-
- cnl = (ai->ai_canonname ? strlen(ai->ai_canonname)+1 : 0);
- l = sizeof(AddrInfoSerialization) + ai->ai_addrlen + cnl;
-
- if (*length + l > maxlength)
- return NULL;
-
- s.ai_flags = ai->ai_flags;
- s.ai_family = ai->ai_family;
- s.ai_socktype = ai->ai_socktype;
- s.ai_protocol = ai->ai_protocol;
- s.ai_addrlen = ai->ai_addrlen;
- s.canonname_len = cnl;
-
- memcpy((uint8_t*) p, &s, sizeof(AddrInfoSerialization));
- memcpy((uint8_t*) p + sizeof(AddrInfoSerialization), ai->ai_addr, ai->ai_addrlen);
-
- if (ai->ai_canonname)
- strcpy((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, ai->ai_canonname);
-
- *length += l;
- return (uint8_t*) p + l;
-}
-
-static int send_addrinfo_reply(int out_fd, unsigned id, int ret, struct addrinfo *ai, int _errno, int _h_errno) {
- AddrInfoResponse data[BUFSIZE/sizeof(AddrInfoResponse) + 1] = {};
- AddrInfoResponse *resp = data;
- assert(out_fd >= 0);
-
- resp->header.type = RESPONSE_ADDRINFO;
- resp->header.id = id;
- resp->header.length = sizeof(AddrInfoResponse);
- resp->ret = ret;
- resp->_errno = _errno;
- resp->_h_errno = _h_errno;
-
- if (ret == 0 && ai) {
- void *p = data + 1;
- struct addrinfo *k;
-
- for (k = ai; k; k = k->ai_next) {
- p = serialize_addrinfo(p, k, &resp->header.length, (char*) data + BUFSIZE - (char*) p);
- if (!p) {
- resp->ret = EAI_MEMORY;
- break;
- }
- }
- }
-
- if (ai)
- freeaddrinfo(ai);
-
- return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
-}
-
-static int send_nameinfo_reply(int out_fd, unsigned id, int ret, const char *host, const char *serv, int _errno, int _h_errno) {
- NameInfoResponse data[BUFSIZE/sizeof(NameInfoResponse) + 1] = {};
- size_t hl, sl;
- NameInfoResponse *resp = data;
-
- assert(out_fd >= 0);
-
- sl = serv ? strlen(serv)+1 : 0;
- hl = host ? strlen(host)+1 : 0;
-
- resp->header.type = RESPONSE_NAMEINFO;
- resp->header.id = id;
- resp->header.length = sizeof(NameInfoResponse) + hl + sl;
- resp->ret = ret;
- resp->_errno = _errno;
- resp->_h_errno = _h_errno;
- resp->hostlen = hl;
- resp->servlen = sl;
-
- assert(sizeof(data) >= resp->header.length);
-
- if (host)
- memcpy((uint8_t *)data + sizeof(NameInfoResponse), host, hl);
-
- if (serv)
- memcpy((uint8_t *)data + sizeof(NameInfoResponse) + hl, serv, sl);
-
- return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
-}
-
-static int send_res_reply(int out_fd, unsigned id, const unsigned char *answer, int ret, int _errno, int _h_errno) {
- ResResponse data[BUFSIZE/sizeof(ResResponse) + 1] = {};
- ResResponse *resp = data;
-
- assert(out_fd >= 0);
-
- resp->header.type = RESPONSE_RES;
- resp->header.id = id;
- resp->header.length = sizeof(ResResponse) + (ret < 0 ? 0 : ret);
- resp->ret = ret;
- resp->_errno = _errno;
- resp->_h_errno = _h_errno;
-
- assert(sizeof(data) >= resp->header.length);
-
- if (ret > 0)
- memcpy((uint8_t *)data + sizeof(ResResponse), answer, ret);
-
- return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
-}
-
-static int handle_request(int out_fd, const Packet *packet, size_t length) {
- const RHeader *req;
- assert(out_fd >= 0);
-
- req = &packet->rheader;
- assert(req);
- assert(length >= sizeof(RHeader));
- assert(length == req->length);
-
- switch (req->type) {
- case REQUEST_ADDRINFO: {
- struct addrinfo ai = {}, *result = NULL;
- const AddrInfoRequest *ai_req = &packet->addrinfo_request;
- const char *node, *service;
- int ret;
-
- assert(length >= sizeof(AddrInfoRequest));
- assert(length == sizeof(AddrInfoRequest) + ai_req->node_len + ai_req->service_len);
-
- ai.ai_flags = ai_req->ai_flags;
- ai.ai_family = ai_req->ai_family;
- ai.ai_socktype = ai_req->ai_socktype;
- ai.ai_protocol = ai_req->ai_protocol;
-
- node = ai_req->node_len ? (const char*) ai_req + sizeof(AddrInfoRequest) : NULL;
- service = ai_req->service_len ? (const char*) ai_req + sizeof(AddrInfoRequest) + ai_req->node_len : NULL;
-
- ret = getaddrinfo(node, service,
- ai_req->hints_is_null ? NULL : &ai,
- &result);
-
- /* send_addrinfo_reply() frees result */
- return send_addrinfo_reply(out_fd, req->id, ret, result, errno, h_errno);
- }
-
- case REQUEST_NAMEINFO: {
- int ret;
- const NameInfoRequest *ni_req = &packet->nameinfo_request;
- char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV];
- struct sockaddr_storage sa;
-
- assert(length >= sizeof(NameInfoRequest));
- assert(length == sizeof(NameInfoRequest) + ni_req->sockaddr_len);
-
- memcpy(&sa, (const uint8_t *) ni_req + sizeof(NameInfoRequest), ni_req->sockaddr_len);
-
- ret = getnameinfo((struct sockaddr *)&sa, ni_req->sockaddr_len,
- ni_req->gethost ? hostbuf : NULL, ni_req->gethost ? sizeof(hostbuf) : 0,
- ni_req->getserv ? servbuf : NULL, ni_req->getserv ? sizeof(servbuf) : 0,
- ni_req->flags);
-
- return send_nameinfo_reply(out_fd, req->id, ret,
- ret == 0 && ni_req->gethost ? hostbuf : NULL,
- ret == 0 && ni_req->getserv ? servbuf : NULL,
- errno, h_errno);
- }
-
- case REQUEST_RES_QUERY:
- case REQUEST_RES_SEARCH: {
- int ret;
- HEADER answer[BUFSIZE/sizeof(HEADER) + 1];
- const ResRequest *res_req = &packet->res_request;
- const char *dname;
-
- assert(length >= sizeof(ResRequest));
- assert(length == sizeof(ResRequest) + res_req->dname_len);
-
- dname = (const char *) req + sizeof(ResRequest);
-
- if (req->type == REQUEST_RES_QUERY)
- ret = res_query(dname, res_req->class, res_req->type, (unsigned char *) answer, BUFSIZE);
- else
- ret = res_search(dname, res_req->class, res_req->type, (unsigned char *) answer, BUFSIZE);
-
- return send_res_reply(out_fd, req->id, (unsigned char *) answer, ret, errno, h_errno);
- }
-
- case REQUEST_TERMINATE:
- /* Quit */
- return -1;
-
- default:
- ;
- }
-
- return 0;
-}
-
-static void* thread_worker(void *p) {
- sd_resolve *resolve = p;
- sigset_t fullset;
-
- /* No signals in this thread please */
- sigfillset(&fullset);
- pthread_sigmask(SIG_BLOCK, &fullset, NULL);
-
- while (!resolve->dead) {
- Packet buf[BUFSIZE/sizeof(Packet) + 1];
- ssize_t length;
-
- length = recv(resolve->fds[REQUEST_RECV_FD], buf, sizeof(buf), 0);
-
- if (length <= 0) {
- if (length < 0 && (errno == EAGAIN || errno == EINTR))
- continue;
- break;
- }
-
- if (resolve->dead)
- break;
-
- if (handle_request(resolve->fds[RESPONSE_SEND_FD], buf, (size_t) length) < 0)
- break;
- }
-
- send_died(resolve->fds[RESPONSE_SEND_FD]);
-
- return NULL;
-}
-
-_public_ sd_resolve* sd_resolve_new(unsigned n_proc) {
- sd_resolve *resolve = NULL;
- int i, r;
-
- assert(n_proc >= 1);
-
- if (n_proc > MAX_WORKERS)
- n_proc = MAX_WORKERS;
-
- resolve = new(sd_resolve, 1);
- if (!resolve) {
- errno = ENOMEM;
- goto fail;
- }
-
- resolve->dead = 0;
- resolve->valid_workers = 0;
-
- for (i = 0; i < MESSAGE_FD_MAX; i++)
- resolve->fds[i] = -1;
-
- memset(resolve->queries, 0, sizeof(resolve->queries));
-
- r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds);
- if (r < 0)
- goto fail;
-
- r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds+2);
- if (r < 0)
- goto fail;
-
- for (resolve->valid_workers = 0; resolve->valid_workers < n_proc; resolve->valid_workers++) {
- r = pthread_create(&resolve->workers[resolve->valid_workers], NULL, thread_worker, resolve);
- if (r) {
- errno = r;
- goto fail;
- }
- }
-
- resolve->current_index = resolve->current_id = 0;
- resolve->done_head = resolve->done_tail = NULL;
- resolve->n_queries = 0;
-
- fd_nonblock(resolve->fds[RESPONSE_RECV_FD], true);
-
- return resolve;
-
-fail:
- if (resolve)
- sd_resolve_free(resolve);
-
- return NULL;
-}
-
-_public_ void sd_resolve_free(sd_resolve *resolve) {
- int i;
- int saved_errno = errno;
- unsigned p;
-
- assert(resolve);
-
- resolve->dead = 1;
-
- if (resolve->fds[REQUEST_SEND_FD] >= 0) {
- RHeader req = {};
-
- req.type = REQUEST_TERMINATE;
- req.length = sizeof(req);
- req.id = 0;
-
- /* Send one termination packet for each worker */
- for (p = 0; p < resolve->valid_workers; p++)
- send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL);
- }
-
- /* Now terminate them and wait until they are gone. */
- for (p = 0; p < resolve->valid_workers; p++) {
- for (;;) {
- if (pthread_join(resolve->workers[p], NULL) != EINTR)
- break;
- }
- }
-
- /* Close all communication channels */
- for (i = 0; i < MESSAGE_FD_MAX; i++)
- if (resolve->fds[i] >= 0)
- close(resolve->fds[i]);
-
- for (p = 0; p < MAX_QUERIES; p++)
- if (resolve->queries[p])
- sd_resolve_cancel(resolve, resolve->queries[p]);
-
- free(resolve);
-
- errno = saved_errno;
-}
-
-_public_ int sd_resolve_fd(sd_resolve *resolve) {
- assert(resolve);
-
- return resolve->fds[RESPONSE_RECV_FD];
-}
-
-static sd_resolve_query *lookup_query(sd_resolve *resolve, unsigned id) {
- sd_resolve_query *q;
- assert(resolve);
-
- q = resolve->queries[id % MAX_QUERIES];
- if (q)
- if (q->id == id)
- return q;
-
- return NULL;
-}
-
-static void complete_query(sd_resolve *resolve, sd_resolve_query *q) {
- assert(resolve);
- assert(q);
- assert(!q->done);
-
- q->done = 1;
-
- if ((q->done_prev = resolve->done_tail))
- resolve->done_tail->done_next = q;
- else
- resolve->done_head = q;
-
- resolve->done_tail = q;
- q->done_next = NULL;
-}
-
-static const void *unserialize_addrinfo(const void *p, struct addrinfo **ret_ai, size_t *length) {
- AddrInfoSerialization s;
- size_t l;
- struct addrinfo *ai;
- assert(p);
- assert(ret_ai);
- assert(length);
-
- if (*length < sizeof(AddrInfoSerialization))
- return NULL;
-
- memcpy(&s, p, sizeof(s));
-
- l = sizeof(AddrInfoSerialization) + s.ai_addrlen + s.canonname_len;
- if (*length < l)
- return NULL;
-
- ai = new(struct addrinfo, 1);
- if (!ai)
- goto fail;
-
- ai->ai_addr = NULL;
- ai->ai_canonname = NULL;
- ai->ai_next = NULL;
-
- if (s.ai_addrlen && !(ai->ai_addr = malloc(s.ai_addrlen)))
- goto fail;
-
- if (s.canonname_len && !(ai->ai_canonname = malloc(s.canonname_len)))
- goto fail;
-
- ai->ai_flags = s.ai_flags;
- ai->ai_family = s.ai_family;
- ai->ai_socktype = s.ai_socktype;
- ai->ai_protocol = s.ai_protocol;
- ai->ai_addrlen = s.ai_addrlen;
-
- if (ai->ai_addr)
- memcpy(ai->ai_addr, (const uint8_t*) p + sizeof(AddrInfoSerialization), s.ai_addrlen);
-
- if (ai->ai_canonname)
- memcpy(ai->ai_canonname, (const uint8_t*) p + sizeof(AddrInfoSerialization) + s.ai_addrlen, s.canonname_len);
-
- *length -= l;
- *ret_ai = ai;
-
- return (const uint8_t*) p + l;
-
-
-fail:
- if (ai)
- sd_resolve_freeaddrinfo(ai);
-
- return NULL;
-}
-
-static int handle_response(sd_resolve *resolve, const Packet *packet, size_t length) {
- const RHeader *resp;
- sd_resolve_query *q;
-
- assert(resolve);
-
- resp = &packet->rheader;
- assert(resp);
- assert(length >= sizeof(RHeader));
- assert(length == resp->length);
-
- if (resp->type == RESPONSE_DIED) {
- resolve->dead = 1;
- return 0;
- }
-
- q = lookup_query(resolve, resp->id);
- if (!q)
- return 0;
-
- switch (resp->type) {
- case RESPONSE_ADDRINFO: {
- const AddrInfoResponse *ai_resp = &packet->addrinfo_response;
- const void *p;
- size_t l;
- struct addrinfo *prev = NULL;
-
- assert(length >= sizeof(AddrInfoResponse));
- assert(q->type == REQUEST_ADDRINFO);
-
- q->ret = ai_resp->ret;
- q->_errno = ai_resp->_errno;
- q->_h_errno = ai_resp->_h_errno;
- l = length - sizeof(AddrInfoResponse);
- p = (const uint8_t*) resp + sizeof(AddrInfoResponse);
-
- while (l > 0 && p) {
- struct addrinfo *ai = NULL;
- p = unserialize_addrinfo(p, &ai, &l);
-
- if (!p || !ai) {
- q->ret = EAI_MEMORY;
- break;
- }
-
- if (prev)
- prev->ai_next = ai;
- else
- q->addrinfo = ai;
-
- prev = ai;
- }
-
- complete_query(resolve, q);
- break;
- }
-
- case RESPONSE_NAMEINFO: {
- const NameInfoResponse *ni_resp = &packet->nameinfo_response;
-
- assert(length >= sizeof(NameInfoResponse));
- assert(q->type == REQUEST_NAMEINFO);
-
- q->ret = ni_resp->ret;
- q->_errno = ni_resp->_errno;
- q->_h_errno = ni_resp->_h_errno;
-
- if (ni_resp->hostlen)
- if (!(q->host = strndup((const char*) ni_resp + sizeof(NameInfoResponse), ni_resp->hostlen-1)))
- q->ret = EAI_MEMORY;
-
- if (ni_resp->servlen)
- if (!(q->serv = strndup((const char*) ni_resp + sizeof(NameInfoResponse) + ni_resp->hostlen, ni_resp->servlen-1)))
- q->ret = EAI_MEMORY;
-
- complete_query(resolve, q);
- break;
- }
-
- case RESPONSE_RES: {
- const ResResponse *res_resp = &packet->res_response;
-
- assert(length >= sizeof(ResResponse));
- assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH);
-
- q->ret = res_resp->ret;
- q->_errno = res_resp->_errno;
- q->_h_errno = res_resp->_h_errno;
-
- if (res_resp->ret >= 0) {
- if (!(q->serv = malloc(res_resp->ret))) {
- q->ret = -1;
- q->_errno = ENOMEM;
- } else
- memcpy(q->serv, (const char *)resp + sizeof(ResResponse), res_resp->ret);
- }
-
- complete_query(resolve, q);
- break;
- }
-
- default:
- ;
- }
-
- return 0;
-}
-
-_public_ int sd_resolve_wait(sd_resolve *resolve, int block) {
- int handled = 0;
- assert(resolve);
-
- for (;;) {
- Packet buf[BUFSIZE/sizeof(Packet) + 1];
- ssize_t l;
-
- if (resolve->dead) {
- errno = ECHILD;
- return -1;
- }
-
- l = recv(resolve->fds[RESPONSE_RECV_FD], buf, sizeof(buf), 0);
- if (l < 0) {
- fd_set fds;
-
- if (errno != EAGAIN)
- return -1;
-
- if (!block || handled)
- return 0;
-
- FD_ZERO(&fds);
- FD_SET(resolve->fds[RESPONSE_RECV_FD], &fds);
-
- if (select(resolve->fds[RESPONSE_RECV_FD]+1, &fds, NULL, NULL, NULL) < 0)
- return -1;
-
- continue;
- }
-
- if (handle_response(resolve, buf, (size_t) l) < 0)
- return -1;
-
- handled = 1;
- }
-}
-
-static sd_resolve_query *alloc_query(sd_resolve *resolve) {
- sd_resolve_query *q;
- assert(resolve);
-
- if (resolve->n_queries >= MAX_QUERIES) {
- errno = ENOMEM;
- return NULL;
- }
-
- while (resolve->queries[resolve->current_index]) {
- resolve->current_index++;
- resolve->current_id++;
-
- while (resolve->current_index >= MAX_QUERIES)
- resolve->current_index -= MAX_QUERIES;
- }
-
- q = resolve->queries[resolve->current_index] = new(sd_resolve_query, 1);
- if (!q) {
- errno = ENOMEM;
- return NULL;
- }
-
- resolve->n_queries++;
-
- q->resolve = resolve;
- q->done = 0;
- q->id = resolve->current_id;
- q->done_next = q->done_prev = NULL;
- q->ret = 0;
- q->_errno = 0;
- q->_h_errno = 0;
- q->addrinfo = NULL;
- q->userdata = NULL;
- q->host = q->serv = NULL;
-
- return q;
-}
-
-_public_ sd_resolve_query* sd_resolve_getaddrinfo(sd_resolve *resolve, const char *node, const char *service, const struct addrinfo *hints) {
- AddrInfoRequest data[BUFSIZE/sizeof(AddrInfoRequest) + 1] = {};
- AddrInfoRequest *req = data;
- sd_resolve_query *q;
- assert(resolve);
- assert(node || service);
-
- if (resolve->dead) {
- errno = ECHILD;
- return NULL;
- }
-
- q = alloc_query(resolve);
- if (!q)
- return NULL;
-
- req->node_len = node ? strlen(node)+1 : 0;
- req->service_len = service ? strlen(service)+1 : 0;
-
- req->header.id = q->id;
- req->header.type = q->type = REQUEST_ADDRINFO;
- req->header.length = sizeof(AddrInfoRequest) + req->node_len + req->service_len;
-
- if (req->header.length > BUFSIZE) {
- errno = ENOMEM;
- goto fail;
- }
-
- if (!(req->hints_is_null = !hints)) {
- req->ai_flags = hints->ai_flags;
- req->ai_family = hints->ai_family;
- req->ai_socktype = hints->ai_socktype;
- req->ai_protocol = hints->ai_protocol;
- }
-
- if (node)
- strcpy((char*) req + sizeof(AddrInfoRequest), node);
-
- if (service)
- strcpy((char*) req + sizeof(AddrInfoRequest) + req->node_len, service);
-
- if (send(resolve->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
- goto fail;
-
- return q;
-
-fail:
- if (q)
- sd_resolve_cancel(resolve, q);
-
- return NULL;
-}
-
-_public_ int sd_resolve_getaddrinfo_done(sd_resolve *resolve, sd_resolve_query* q, struct addrinfo **ret_res) {
- int ret;
- assert(resolve);
- assert(q);
- assert(q->resolve == resolve);
- assert(q->type == REQUEST_ADDRINFO);
-
- if (resolve->dead) {
- errno = ECHILD;
- return EAI_SYSTEM;
- }
-
- if (!q->done)
- return EAI_AGAIN;
-
- *ret_res = q->addrinfo;
- q->addrinfo = NULL;
-
- ret = q->ret;
-
- if (ret == EAI_SYSTEM)
- errno = q->_errno;
-
- if (ret != 0)
- h_errno = q->_h_errno;
-
- sd_resolve_cancel(resolve, q);
-
- return ret;
-}
-
-_public_ sd_resolve_query* sd_resolve_getnameinfo(sd_resolve *resolve, const struct sockaddr *sa, socklen_t salen, int flags, int gethost, int getserv) {
- NameInfoRequest data[BUFSIZE/sizeof(NameInfoRequest) + 1] = {};
- NameInfoRequest *req = data;
- sd_resolve_query *q;
-
- assert(resolve);
- assert(sa);
- assert(salen > 0);
-
- if (resolve->dead) {
- errno = ECHILD;
- return NULL;
- }
-
- q = alloc_query(resolve);
- if (!q)
- return NULL;
-
- req->header.id = q->id;
- req->header.type = q->type = REQUEST_NAMEINFO;
- req->header.length = sizeof(NameInfoRequest) + salen;
-
- if (req->header.length > BUFSIZE) {
- errno = ENOMEM;
- goto fail;
- }
-
- req->flags = flags;
- req->sockaddr_len = salen;
- req->gethost = gethost;
- req->getserv = getserv;
-
- memcpy((uint8_t*) req + sizeof(NameInfoRequest), sa, salen);
-
- if (send(resolve->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
- goto fail;
-
- return q;
-
-fail:
- if (q)
- sd_resolve_cancel(resolve, q);
-
- return NULL;
-}
-
-_public_ int sd_resolve_getnameinfo_done(sd_resolve *resolve, sd_resolve_query* q, char *ret_host, size_t hostlen, char *ret_serv, size_t servlen) {
- int ret;
- assert(resolve);
- assert(q);
- assert(q->resolve == resolve);
- assert(q->type == REQUEST_NAMEINFO);
- assert(!ret_host || hostlen);
- assert(!ret_serv || servlen);
-
- if (resolve->dead) {
- errno = ECHILD;
- return EAI_SYSTEM;
- }
-
- if (!q->done)
- return EAI_AGAIN;
-
- if (ret_host && q->host) {
- strncpy(ret_host, q->host, hostlen);
- ret_host[hostlen-1] = 0;
- }
-
- if (ret_serv && q->serv) {
- strncpy(ret_serv, q->serv, servlen);
- ret_serv[servlen-1] = 0;
- }
-
- ret = q->ret;
-
- if (ret == EAI_SYSTEM)
- errno = q->_errno;
-
- if (ret != 0)
- h_errno = q->_h_errno;
-
- sd_resolve_cancel(resolve, q);
-
- return ret;
-}
-
-static sd_resolve_query * resolve_res(sd_resolve *resolve, QueryType qtype, const char *dname, int class, int type) {
- ResRequest data[BUFSIZE/sizeof(ResRequest) + 1];
- ResRequest *req = data;
- sd_resolve_query *q;
-
- assert(resolve);
- assert(dname);
-
- if (resolve->dead) {
- errno = ECHILD;
- return NULL;
- }
-
- q = alloc_query(resolve);
- if (!q)
- return NULL;
-
- req->dname_len = strlen(dname) + 1;
-
- req->header.id = q->id;
- req->header.type = q->type = qtype;
- req->header.length = sizeof(ResRequest) + req->dname_len;
-
- if (req->header.length > BUFSIZE) {
- errno = ENOMEM;
- goto fail;
- }
-
- req->class = class;
- req->type = type;
-
- strcpy((char*) req + sizeof(ResRequest), dname);
-
- if (send(resolve->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
- goto fail;
-
- return q;
-
-fail:
- if (q)
- sd_resolve_cancel(resolve, q);
-
- return NULL;
-}
-
-_public_ sd_resolve_query* sd_resolve_res_query(sd_resolve *resolve, const char *dname, int class, int type) {
- return resolve_res(resolve, REQUEST_RES_QUERY, dname, class, type);
-}
-
-_public_ sd_resolve_query* sd_resolve_res_search(sd_resolve *resolve, const char *dname, int class, int type) {
- return resolve_res(resolve, REQUEST_RES_SEARCH, dname, class, type);
-}
-
-_public_ int sd_resolve_res_done(sd_resolve *resolve, sd_resolve_query* q, unsigned char **answer) {
- int ret;
- assert(resolve);
- assert(q);
- assert(q->resolve == resolve);
- assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH);
- assert(answer);
-
- if (resolve->dead) {
- errno = ECHILD;
- return -ECHILD;
- }
-
- if (!q->done) {
- errno = EAGAIN;
- return -EAGAIN;
- }
-
- *answer = (unsigned char *)q->serv;
- q->serv = NULL;
-
- ret = q->ret;
-
- if (ret < 0) {
- errno = q->_errno;
- h_errno = q->_h_errno;
- }
-
- sd_resolve_cancel(resolve, q);
-
- return ret < 0 ? -errno : ret;
-}
-
-_public_ sd_resolve_query* sd_resolve_get_next(sd_resolve *resolve) {
- assert(resolve);
- return resolve->done_head;
-}
-
-_public_ int sd_resolve_get_n_queries(sd_resolve *resolve) {
- assert(resolve);
- return resolve->n_queries;
-}
-
-_public_ void sd_resolve_cancel(sd_resolve *resolve, sd_resolve_query* q) {
- int i;
- int saved_errno = errno;
-
- assert(resolve);
- assert(q);
- assert(q->resolve == resolve);
- assert(resolve->n_queries > 0);
-
- if (q->done) {
-
- if (q->done_prev)
- q->done_prev->done_next = q->done_next;
- else
- resolve->done_head = q->done_next;
-
- if (q->done_next)
- q->done_next->done_prev = q->done_prev;
- else
- resolve->done_tail = q->done_prev;
- }
-
- i = q->id % MAX_QUERIES;
- assert(resolve->queries[i] == q);
- resolve->queries[i] = NULL;
-
- sd_resolve_freeaddrinfo(q->addrinfo);
- free(q->host);
- free(q->serv);
-
- resolve->n_queries--;
- free(q);
-
- errno = saved_errno;
-}
-
-_public_ void sd_resolve_freeaddrinfo(struct addrinfo *ai) {
- int saved_errno = errno;
-
- while (ai) {
- struct addrinfo *next = ai->ai_next;
-
- free(ai->ai_addr);
- free(ai->ai_canonname);
- free(ai);
-
- ai = next;
- }
-
- errno = saved_errno;
-}
-
-_public_ int sd_resolve_isdone(sd_resolve *resolve, sd_resolve_query*q) {
- assert(resolve);
- assert(q);
- assert(q->resolve == resolve);
-
- return q->done;
-}
-
-_public_ void sd_resolve_setuserdata(sd_resolve *resolve, sd_resolve_query *q, void *userdata) {
- assert(q);
- assert(resolve);
- assert(q->resolve = resolve);
-
- q->userdata = userdata;
-}
-
-_public_ void* sd_resolve_getuserdata(sd_resolve *resolve, sd_resolve_query *q) {
- assert(q);
- assert(resolve);
- assert(q->resolve = resolve);
-
- return q->userdata;
-}
diff --git a/src/libsystemd/sd-resolve/Makefile b/src/libsystemd/sd-resolve/Makefile
new file mode 120000
index 0000000..94aaae2
--- /dev/null
+++ b/src/libsystemd/sd-resolve/Makefile
@@ -0,0 +1 @@
+../../Makefile
\ No newline at end of file
diff --git a/src/libsystemd/sd-resolve/resolve-util.h b/src/libsystemd/sd-resolve/resolve-util.h
new file mode 100644
index 0000000..58df738
--- /dev/null
+++ b/src/libsystemd/sd-resolve/resolve-util.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Daniel Buch
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_resolve*, sd_resolve_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct addrinfo*, sd_resolve_freeaddrinfo);
+#define _cleanup_resolve_free_ _cleanup_(sd_resolve_freep)
+#define _cleanup_resolve_addrinfo_free_ _cleanup_(sd_resolve_freeaddrinfop)
diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c
new file mode 100644
index 0000000..464967b
--- /dev/null
+++ b/src/libsystemd/sd-resolve/sd-resolve.c
@@ -0,0 +1,1130 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2005-2008 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <stdint.h>
+#include <pthread.h>
+#include <sys/prctl.h>
+
+#include "sd-resolve.h"
+#include "util.h"
+
+#define MAX_WORKERS 16
+#define MAX_QUERIES 256
+#define BUFSIZE (10240)
+
+typedef enum {
+ REQUEST_ADDRINFO,
+ RESPONSE_ADDRINFO,
+ REQUEST_NAMEINFO,
+ RESPONSE_NAMEINFO,
+ REQUEST_RES_QUERY,
+ REQUEST_RES_SEARCH,
+ RESPONSE_RES,
+ REQUEST_TERMINATE,
+ RESPONSE_DIED
+} QueryType;
+
+enum {
+ REQUEST_RECV_FD = 0,
+ REQUEST_SEND_FD = 1,
+ RESPONSE_RECV_FD = 2,
+ RESPONSE_SEND_FD = 3,
+ MESSAGE_FD_MAX = 4
+};
+
+struct sd_resolve {
+ int fds[MESSAGE_FD_MAX];
+
+ pthread_t workers[MAX_WORKERS];
+ unsigned valid_workers;
+
+ unsigned current_id, current_index;
+ sd_resolve_query* queries[MAX_QUERIES];
+
+ sd_resolve_query *done_head, *done_tail;
+
+ int n_queries;
+ int dead;
+};
+
+struct sd_resolve_query {
+ sd_resolve *resolve;
+ int done;
+ unsigned id;
+ QueryType type;
+ sd_resolve_query *done_next, *done_prev;
+ int ret;
+ int _errno;
+ int _h_errno;
+ struct addrinfo *addrinfo;
+ char *serv, *host;
+ void *userdata;
+};
+
+typedef struct RHeader {
+ QueryType type;
+ unsigned id;
+ size_t length;
+} RHeader;
+
+typedef struct AddrInfoRequest {
+ struct RHeader header;
+ int hints_is_null;
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t node_len, service_len;
+} AddrInfoRequest;
+
+typedef struct AddrInfoResponse {
+ struct RHeader header;
+ int ret;
+ int _errno;
+ int _h_errno;
+ /* followed by addrinfo_serialization[] */
+} AddrInfoResponse;
+
+typedef struct AddrInfoSerialization {
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ size_t canonname_len;
+ /* Followed by ai_addr amd ai_canonname with variable lengths */
+} AddrInfoSerialization;
+
+typedef struct NameInfoRequest {
+ struct RHeader header;
+ int flags;
+ socklen_t sockaddr_len;
+ int gethost, getserv;
+} NameInfoRequest;
+
+typedef struct NameInfoResponse {
+ struct RHeader header;
+ size_t hostlen, servlen;
+ int ret;
+ int _errno;
+ int _h_errno;
+} NameInfoResponse;
+
+typedef struct ResRequest {
+ struct RHeader header;
+ int class;
+ int type;
+ size_t dname_len;
+} ResRequest;
+
+typedef struct ResResponse {
+ struct RHeader header;
+ int ret;
+ int _errno;
+ int _h_errno;
+} ResResponse;
+
+typedef union Packet {
+ RHeader rheader;
+ AddrInfoRequest addrinfo_request;
+ AddrInfoResponse addrinfo_response;
+ NameInfoRequest nameinfo_request;
+ NameInfoResponse nameinfo_response;
+ ResRequest res_request;
+ ResResponse res_response;
+} Packet;
+
+static int send_died(int out_fd) {
+ RHeader rh = {};
+ assert(out_fd > 0);
+
+ rh.type = RESPONSE_DIED;
+ rh.id = 0;
+ rh.length = sizeof(rh);
+
+ return send(out_fd, &rh, rh.length, MSG_NOSIGNAL);
+}
+
+static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *length, size_t maxlength) {
+ AddrInfoSerialization s;
+ size_t cnl, l;
+ assert(p);
+ assert(ai);
+ assert(length);
+ assert(*length <= maxlength);
+
+ cnl = (ai->ai_canonname ? strlen(ai->ai_canonname)+1 : 0);
+ l = sizeof(AddrInfoSerialization) + ai->ai_addrlen + cnl;
+
+ if (*length + l > maxlength)
+ return NULL;
+
+ s.ai_flags = ai->ai_flags;
+ s.ai_family = ai->ai_family;
+ s.ai_socktype = ai->ai_socktype;
+ s.ai_protocol = ai->ai_protocol;
+ s.ai_addrlen = ai->ai_addrlen;
+ s.canonname_len = cnl;
+
+ memcpy((uint8_t*) p, &s, sizeof(AddrInfoSerialization));
+ memcpy((uint8_t*) p + sizeof(AddrInfoSerialization), ai->ai_addr, ai->ai_addrlen);
+
+ if (ai->ai_canonname)
+ strcpy((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, ai->ai_canonname);
+
+ *length += l;
+ return (uint8_t*) p + l;
+}
+
+static int send_addrinfo_reply(int out_fd, unsigned id, int ret, struct addrinfo *ai, int _errno, int _h_errno) {
+ AddrInfoResponse data[BUFSIZE/sizeof(AddrInfoResponse) + 1] = {};
+ AddrInfoResponse *resp = data;
+ assert(out_fd >= 0);
+
+ resp->header.type = RESPONSE_ADDRINFO;
+ resp->header.id = id;
+ resp->header.length = sizeof(AddrInfoResponse);
+ resp->ret = ret;
+ resp->_errno = _errno;
+ resp->_h_errno = _h_errno;
+
+ if (ret == 0 && ai) {
+ void *p = data + 1;
+ struct addrinfo *k;
+
+ for (k = ai; k; k = k->ai_next) {
+ p = serialize_addrinfo(p, k, &resp->header.length, (char*) data + BUFSIZE - (char*) p);
+ if (!p) {
+ resp->ret = EAI_MEMORY;
+ break;
+ }
+ }
+ }
+
+ if (ai)
+ freeaddrinfo(ai);
+
+ return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
+}
+
+static int send_nameinfo_reply(int out_fd, unsigned id, int ret, const char *host, const char *serv, int _errno, int _h_errno) {
+ NameInfoResponse data[BUFSIZE/sizeof(NameInfoResponse) + 1] = {};
+ size_t hl, sl;
+ NameInfoResponse *resp = data;
+
+ assert(out_fd >= 0);
+
+ sl = serv ? strlen(serv)+1 : 0;
+ hl = host ? strlen(host)+1 : 0;
+
+ resp->header.type = RESPONSE_NAMEINFO;
+ resp->header.id = id;
+ resp->header.length = sizeof(NameInfoResponse) + hl + sl;
+ resp->ret = ret;
+ resp->_errno = _errno;
+ resp->_h_errno = _h_errno;
+ resp->hostlen = hl;
+ resp->servlen = sl;
+
+ assert(sizeof(data) >= resp->header.length);
+
+ if (host)
+ memcpy((uint8_t *)data + sizeof(NameInfoResponse), host, hl);
+
+ if (serv)
+ memcpy((uint8_t *)data + sizeof(NameInfoResponse) + hl, serv, sl);
+
+ return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
+}
+
+static int send_res_reply(int out_fd, unsigned id, const unsigned char *answer, int ret, int _errno, int _h_errno) {
+ ResResponse data[BUFSIZE/sizeof(ResResponse) + 1] = {};
+ ResResponse *resp = data;
+
+ assert(out_fd >= 0);
+
+ resp->header.type = RESPONSE_RES;
+ resp->header.id = id;
+ resp->header.length = sizeof(ResResponse) + (ret < 0 ? 0 : ret);
+ resp->ret = ret;
+ resp->_errno = _errno;
+ resp->_h_errno = _h_errno;
+
+ assert(sizeof(data) >= resp->header.length);
+
+ if (ret > 0)
+ memcpy((uint8_t *)data + sizeof(ResResponse), answer, ret);
+
+ return send(out_fd, resp, resp->header.length, MSG_NOSIGNAL);
+}
+
+static int handle_request(int out_fd, const Packet *packet, size_t length) {
+ const RHeader *req;
+ assert(out_fd >= 0);
+
+ req = &packet->rheader;
+ assert(req);
+ assert(length >= sizeof(RHeader));
+ assert(length == req->length);
+
+ switch (req->type) {
+ case REQUEST_ADDRINFO: {
+ struct addrinfo ai = {}, *result = NULL;
+ const AddrInfoRequest *ai_req = &packet->addrinfo_request;
+ const char *node, *service;
+ int ret;
+
+ assert(length >= sizeof(AddrInfoRequest));
+ assert(length == sizeof(AddrInfoRequest) + ai_req->node_len + ai_req->service_len);
+
+ ai.ai_flags = ai_req->ai_flags;
+ ai.ai_family = ai_req->ai_family;
+ ai.ai_socktype = ai_req->ai_socktype;
+ ai.ai_protocol = ai_req->ai_protocol;
+
+ node = ai_req->node_len ? (const char*) ai_req + sizeof(AddrInfoRequest) : NULL;
+ service = ai_req->service_len ? (const char*) ai_req + sizeof(AddrInfoRequest) + ai_req->node_len : NULL;
+
+ ret = getaddrinfo(node, service,
+ ai_req->hints_is_null ? NULL : &ai,
+ &result);
+
+ /* send_addrinfo_reply() frees result */
+ return send_addrinfo_reply(out_fd, req->id, ret, result, errno, h_errno);
+ }
+
+ case REQUEST_NAMEINFO: {
+ int ret;
+ const NameInfoRequest *ni_req = &packet->nameinfo_request;
+ char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV];
+ struct sockaddr_storage sa;
+
+ assert(length >= sizeof(NameInfoRequest));
+ assert(length == sizeof(NameInfoRequest) + ni_req->sockaddr_len);
+
+ memcpy(&sa, (const uint8_t *) ni_req + sizeof(NameInfoRequest), ni_req->sockaddr_len);
+
+ ret = getnameinfo((struct sockaddr *)&sa, ni_req->sockaddr_len,
+ ni_req->gethost ? hostbuf : NULL, ni_req->gethost ? sizeof(hostbuf) : 0,
+ ni_req->getserv ? servbuf : NULL, ni_req->getserv ? sizeof(servbuf) : 0,
+ ni_req->flags);
+
+ return send_nameinfo_reply(out_fd, req->id, ret,
+ ret == 0 && ni_req->gethost ? hostbuf : NULL,
+ ret == 0 && ni_req->getserv ? servbuf : NULL,
+ errno, h_errno);
+ }
+
+ case REQUEST_RES_QUERY:
+ case REQUEST_RES_SEARCH: {
+ int ret;
+ HEADER answer[BUFSIZE/sizeof(HEADER) + 1];
+ const ResRequest *res_req = &packet->res_request;
+ const char *dname;
+
+ assert(length >= sizeof(ResRequest));
+ assert(length == sizeof(ResRequest) + res_req->dname_len);
+
+ dname = (const char *) req + sizeof(ResRequest);
+
+ if (req->type == REQUEST_RES_QUERY)
+ ret = res_query(dname, res_req->class, res_req->type, (unsigned char *) answer, BUFSIZE);
+ else
+ ret = res_search(dname, res_req->class, res_req->type, (unsigned char *) answer, BUFSIZE);
+
+ return send_res_reply(out_fd, req->id, (unsigned char *) answer, ret, errno, h_errno);
+ }
+
+ case REQUEST_TERMINATE:
+ /* Quit */
+ return -1;
+
+ default:
+ ;
+ }
+
+ return 0;
+}
+
+static void* thread_worker(void *p) {
+ sd_resolve *resolve = p;
+ sigset_t fullset;
+
+ /* No signals in this thread please */
+ sigfillset(&fullset);
+ pthread_sigmask(SIG_BLOCK, &fullset, NULL);
+
+ while (!resolve->dead) {
+ Packet buf[BUFSIZE/sizeof(Packet) + 1];
+ ssize_t length;
+
+ length = recv(resolve->fds[REQUEST_RECV_FD], buf, sizeof(buf), 0);
+
+ if (length <= 0) {
+ if (length < 0 && (errno == EAGAIN || errno == EINTR))
+ continue;
+ break;
+ }
+
+ if (resolve->dead)
+ break;
+
+ if (handle_request(resolve->fds[RESPONSE_SEND_FD], buf, (size_t) length) < 0)
+ break;
+ }
+
+ send_died(resolve->fds[RESPONSE_SEND_FD]);
+
+ return NULL;
+}
+
+_public_ sd_resolve* sd_resolve_new(unsigned n_proc) {
+ sd_resolve *resolve = NULL;
+ int i, r;
+
+ assert(n_proc >= 1);
+
+ if (n_proc > MAX_WORKERS)
+ n_proc = MAX_WORKERS;
+
+ resolve = new(sd_resolve, 1);
+ if (!resolve) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ resolve->dead = 0;
+ resolve->valid_workers = 0;
+
+ for (i = 0; i < MESSAGE_FD_MAX; i++)
+ resolve->fds[i] = -1;
+
+ memset(resolve->queries, 0, sizeof(resolve->queries));
+
+ r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds);
+ if (r < 0)
+ goto fail;
+
+ r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds+2);
+ if (r < 0)
+ goto fail;
+
+ for (resolve->valid_workers = 0; resolve->valid_workers < n_proc; resolve->valid_workers++) {
+ r = pthread_create(&resolve->workers[resolve->valid_workers], NULL, thread_worker, resolve);
+ if (r) {
+ errno = r;
+ goto fail;
+ }
+ }
+
+ resolve->current_index = resolve->current_id = 0;
+ resolve->done_head = resolve->done_tail = NULL;
+ resolve->n_queries = 0;
+
+ fd_nonblock(resolve->fds[RESPONSE_RECV_FD], true);
+
+ return resolve;
+
+fail:
+ if (resolve)
+ sd_resolve_free(resolve);
+
+ return NULL;
+}
+
+_public_ void sd_resolve_free(sd_resolve *resolve) {
+ int i;
+ int saved_errno = errno;
+ unsigned p;
+
+ assert(resolve);
+
+ resolve->dead = 1;
+
+ if (resolve->fds[REQUEST_SEND_FD] >= 0) {
+ RHeader req = {};
+
+ req.type = REQUEST_TERMINATE;
+ req.length = sizeof(req);
+ req.id = 0;
+
+ /* Send one termination packet for each worker */
+ for (p = 0; p < resolve->valid_workers; p++)
+ send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL);
+ }
+
+ /* Now terminate them and wait until they are gone. */
+ for (p = 0; p < resolve->valid_workers; p++) {
+ for (;;) {
+ if (pthread_join(resolve->workers[p], NULL) != EINTR)
+ break;
+ }
+ }
+
+ /* Close all communication channels */
+ for (i = 0; i < MESSAGE_FD_MAX; i++)
+ if (resolve->fds[i] >= 0)
+ close(resolve->fds[i]);
+
+ for (p = 0; p < MAX_QUERIES; p++)
+ if (resolve->queries[p])
+ sd_resolve_cancel(resolve, resolve->queries[p]);
+
+ free(resolve);
+
+ errno = saved_errno;
+}
+
+_public_ int sd_resolve_fd(sd_resolve *resolve) {
+ assert(resolve);
+
+ return resolve->fds[RESPONSE_RECV_FD];
+}
+
+static sd_resolve_query *lookup_query(sd_resolve *resolve, unsigned id) {
+ sd_resolve_query *q;
+ assert(resolve);
+
+ q = resolve->queries[id % MAX_QUERIES];
+ if (q)
+ if (q->id == id)
+ return q;
+
+ return NULL;
+}
+
+static void complete_query(sd_resolve *resolve, sd_resolve_query *q) {
+ assert(resolve);
+ assert(q);
+ assert(!q->done);
+
+ q->done = 1;
+
+ if ((q->done_prev = resolve->done_tail))
+ resolve->done_tail->done_next = q;
+ else
+ resolve->done_head = q;
+
+ resolve->done_tail = q;
+ q->done_next = NULL;
+}
+
+static const void *unserialize_addrinfo(const void *p, struct addrinfo **ret_ai, size_t *length) {
+ AddrInfoSerialization s;
+ size_t l;
+ struct addrinfo *ai;
+ assert(p);
+ assert(ret_ai);
+ assert(length);
+
+ if (*length < sizeof(AddrInfoSerialization))
+ return NULL;
+
+ memcpy(&s, p, sizeof(s));
+
+ l = sizeof(AddrInfoSerialization) + s.ai_addrlen + s.canonname_len;
+ if (*length < l)
+ return NULL;
+
+ ai = new(struct addrinfo, 1);
+ if (!ai)
+ goto fail;
+
+ ai->ai_addr = NULL;
+ ai->ai_canonname = NULL;
+ ai->ai_next = NULL;
+
+ if (s.ai_addrlen && !(ai->ai_addr = malloc(s.ai_addrlen)))
+ goto fail;
+
+ if (s.canonname_len && !(ai->ai_canonname = malloc(s.canonname_len)))
+ goto fail;
+
+ ai->ai_flags = s.ai_flags;
+ ai->ai_family = s.ai_family;
+ ai->ai_socktype = s.ai_socktype;
+ ai->ai_protocol = s.ai_protocol;
+ ai->ai_addrlen = s.ai_addrlen;
+
+ if (ai->ai_addr)
+ memcpy(ai->ai_addr, (const uint8_t*) p + sizeof(AddrInfoSerialization), s.ai_addrlen);
+
+ if (ai->ai_canonname)
+ memcpy(ai->ai_canonname, (const uint8_t*) p + sizeof(AddrInfoSerialization) + s.ai_addrlen, s.canonname_len);
+
+ *length -= l;
+ *ret_ai = ai;
+
+ return (const uint8_t*) p + l;
+
+
+fail:
+ if (ai)
+ sd_resolve_freeaddrinfo(ai);
+
+ return NULL;
+}
+
+static int handle_response(sd_resolve *resolve, const Packet *packet, size_t length) {
+ const RHeader *resp;
+ sd_resolve_query *q;
+
+ assert(resolve);
+
+ resp = &packet->rheader;
+ assert(resp);
+ assert(length >= sizeof(RHeader));
+ assert(length == resp->length);
+
+ if (resp->type == RESPONSE_DIED) {
+ resolve->dead = 1;
+ return 0;
+ }
+
+ q = lookup_query(resolve, resp->id);
+ if (!q)
+ return 0;
+
+ switch (resp->type) {
+ case RESPONSE_ADDRINFO: {
+ const AddrInfoResponse *ai_resp = &packet->addrinfo_response;
+ const void *p;
+ size_t l;
+ struct addrinfo *prev = NULL;
+
+ assert(length >= sizeof(AddrInfoResponse));
+ assert(q->type == REQUEST_ADDRINFO);
+
+ q->ret = ai_resp->ret;
+ q->_errno = ai_resp->_errno;
+ q->_h_errno = ai_resp->_h_errno;
+ l = length - sizeof(AddrInfoResponse);
+ p = (const uint8_t*) resp + sizeof(AddrInfoResponse);
+
+ while (l > 0 && p) {
+ struct addrinfo *ai = NULL;
+ p = unserialize_addrinfo(p, &ai, &l);
+
+ if (!p || !ai) {
+ q->ret = EAI_MEMORY;
+ break;
+ }
+
+ if (prev)
+ prev->ai_next = ai;
+ else
+ q->addrinfo = ai;
+
+ prev = ai;
+ }
+
+ complete_query(resolve, q);
+ break;
+ }
+
+ case RESPONSE_NAMEINFO: {
+ const NameInfoResponse *ni_resp = &packet->nameinfo_response;
+
+ assert(length >= sizeof(NameInfoResponse));
+ assert(q->type == REQUEST_NAMEINFO);
+
+ q->ret = ni_resp->ret;
+ q->_errno = ni_resp->_errno;
+ q->_h_errno = ni_resp->_h_errno;
+
+ if (ni_resp->hostlen)
+ if (!(q->host = strndup((const char*) ni_resp + sizeof(NameInfoResponse), ni_resp->hostlen-1)))
+ q->ret = EAI_MEMORY;
+
+ if (ni_resp->servlen)
+ if (!(q->serv = strndup((const char*) ni_resp + sizeof(NameInfoResponse) + ni_resp->hostlen, ni_resp->servlen-1)))
+ q->ret = EAI_MEMORY;
+
+ complete_query(resolve, q);
+ break;
+ }
+
+ case RESPONSE_RES: {
+ const ResResponse *res_resp = &packet->res_response;
+
+ assert(length >= sizeof(ResResponse));
+ assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH);
+
+ q->ret = res_resp->ret;
+ q->_errno = res_resp->_errno;
+ q->_h_errno = res_resp->_h_errno;
+
+ if (res_resp->ret >= 0) {
+ if (!(q->serv = malloc(res_resp->ret))) {
+ q->ret = -1;
+ q->_errno = ENOMEM;
+ } else
+ memcpy(q->serv, (const char *)resp + sizeof(ResResponse), res_resp->ret);
+ }
+
+ complete_query(resolve, q);
+ break;
+ }
+
+ default:
+ ;
+ }
+
+ return 0;
+}
+
+_public_ int sd_resolve_wait(sd_resolve *resolve, int block) {
+ int handled = 0;
+ assert(resolve);
+
+ for (;;) {
+ Packet buf[BUFSIZE/sizeof(Packet) + 1];
+ ssize_t l;
+
+ if (resolve->dead) {
+ errno = ECHILD;
+ return -1;
+ }
+
+ l = recv(resolve->fds[RESPONSE_RECV_FD], buf, sizeof(buf), 0);
+ if (l < 0) {
+ fd_set fds;
+
+ if (errno != EAGAIN)
+ return -1;
+
+ if (!block || handled)
+ return 0;
+
+ FD_ZERO(&fds);
+ FD_SET(resolve->fds[RESPONSE_RECV_FD], &fds);
+
+ if (select(resolve->fds[RESPONSE_RECV_FD]+1, &fds, NULL, NULL, NULL) < 0)
+ return -1;
+
+ continue;
+ }
+
+ if (handle_response(resolve, buf, (size_t) l) < 0)
+ return -1;
+
+ handled = 1;
+ }
+}
+
+static sd_resolve_query *alloc_query(sd_resolve *resolve) {
+ sd_resolve_query *q;
+ assert(resolve);
+
+ if (resolve->n_queries >= MAX_QUERIES) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ while (resolve->queries[resolve->current_index]) {
+ resolve->current_index++;
+ resolve->current_id++;
+
+ while (resolve->current_index >= MAX_QUERIES)
+ resolve->current_index -= MAX_QUERIES;
+ }
+
+ q = resolve->queries[resolve->current_index] = new(sd_resolve_query, 1);
+ if (!q) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ resolve->n_queries++;
+
+ q->resolve = resolve;
+ q->done = 0;
+ q->id = resolve->current_id;
+ q->done_next = q->done_prev = NULL;
+ q->ret = 0;
+ q->_errno = 0;
+ q->_h_errno = 0;
+ q->addrinfo = NULL;
+ q->userdata = NULL;
+ q->host = q->serv = NULL;
+
+ return q;
+}
+
+_public_ sd_resolve_query* sd_resolve_getaddrinfo(sd_resolve *resolve, const char *node, const char *service, const struct addrinfo *hints) {
+ AddrInfoRequest data[BUFSIZE/sizeof(AddrInfoRequest) + 1] = {};
+ AddrInfoRequest *req = data;
+ sd_resolve_query *q;
+ assert(resolve);
+ assert(node || service);
+
+ if (resolve->dead) {
+ errno = ECHILD;
+ return NULL;
+ }
+
+ q = alloc_query(resolve);
+ if (!q)
+ return NULL;
+
+ req->node_len = node ? strlen(node)+1 : 0;
+ req->service_len = service ? strlen(service)+1 : 0;
+
+ req->header.id = q->id;
+ req->header.type = q->type = REQUEST_ADDRINFO;
+ req->header.length = sizeof(AddrInfoRequest) + req->node_len + req->service_len;
+
+ if (req->header.length > BUFSIZE) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ if (!(req->hints_is_null = !hints)) {
+ req->ai_flags = hints->ai_flags;
+ req->ai_family = hints->ai_family;
+ req->ai_socktype = hints->ai_socktype;
+ req->ai_protocol = hints->ai_protocol;
+ }
+
+ if (node)
+ strcpy((char*) req + sizeof(AddrInfoRequest), node);
+
+ if (service)
+ strcpy((char*) req + sizeof(AddrInfoRequest) + req->node_len, service);
+
+ if (send(resolve->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
+ goto fail;
+
+ return q;
+
+fail:
+ if (q)
+ sd_resolve_cancel(resolve, q);
+
+ return NULL;
+}
+
+_public_ int sd_resolve_getaddrinfo_done(sd_resolve *resolve, sd_resolve_query* q, struct addrinfo **ret_res) {
+ int ret;
+ assert(resolve);
+ assert(q);
+ assert(q->resolve == resolve);
+ assert(q->type == REQUEST_ADDRINFO);
+
+ if (resolve->dead) {
+ errno = ECHILD;
+ return EAI_SYSTEM;
+ }
+
+ if (!q->done)
+ return EAI_AGAIN;
+
+ *ret_res = q->addrinfo;
+ q->addrinfo = NULL;
+
+ ret = q->ret;
+
+ if (ret == EAI_SYSTEM)
+ errno = q->_errno;
+
+ if (ret != 0)
+ h_errno = q->_h_errno;
+
+ sd_resolve_cancel(resolve, q);
+
+ return ret;
+}
+
+_public_ sd_resolve_query* sd_resolve_getnameinfo(sd_resolve *resolve, const struct sockaddr *sa, socklen_t salen, int flags, int gethost, int getserv) {
+ NameInfoRequest data[BUFSIZE/sizeof(NameInfoRequest) + 1] = {};
+ NameInfoRequest *req = data;
+ sd_resolve_query *q;
+
+ assert(resolve);
+ assert(sa);
+ assert(salen > 0);
+
+ if (resolve->dead) {
+ errno = ECHILD;
+ return NULL;
+ }
+
+ q = alloc_query(resolve);
+ if (!q)
+ return NULL;
+
+ req->header.id = q->id;
+ req->header.type = q->type = REQUEST_NAMEINFO;
+ req->header.length = sizeof(NameInfoRequest) + salen;
+
+ if (req->header.length > BUFSIZE) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ req->flags = flags;
+ req->sockaddr_len = salen;
+ req->gethost = gethost;
+ req->getserv = getserv;
+
+ memcpy((uint8_t*) req + sizeof(NameInfoRequest), sa, salen);
+
+ if (send(resolve->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
+ goto fail;
+
+ return q;
+
+fail:
+ if (q)
+ sd_resolve_cancel(resolve, q);
+
+ return NULL;
+}
+
+_public_ int sd_resolve_getnameinfo_done(sd_resolve *resolve, sd_resolve_query* q, char *ret_host, size_t hostlen, char *ret_serv, size_t servlen) {
+ int ret;
+ assert(resolve);
+ assert(q);
+ assert(q->resolve == resolve);
+ assert(q->type == REQUEST_NAMEINFO);
+ assert(!ret_host || hostlen);
+ assert(!ret_serv || servlen);
+
+ if (resolve->dead) {
+ errno = ECHILD;
+ return EAI_SYSTEM;
+ }
+
+ if (!q->done)
+ return EAI_AGAIN;
+
+ if (ret_host && q->host) {
+ strncpy(ret_host, q->host, hostlen);
+ ret_host[hostlen-1] = 0;
+ }
+
+ if (ret_serv && q->serv) {
+ strncpy(ret_serv, q->serv, servlen);
+ ret_serv[servlen-1] = 0;
+ }
+
+ ret = q->ret;
+
+ if (ret == EAI_SYSTEM)
+ errno = q->_errno;
+
+ if (ret != 0)
+ h_errno = q->_h_errno;
+
+ sd_resolve_cancel(resolve, q);
+
+ return ret;
+}
+
+static sd_resolve_query * resolve_res(sd_resolve *resolve, QueryType qtype, const char *dname, int class, int type) {
+ ResRequest data[BUFSIZE/sizeof(ResRequest) + 1];
+ ResRequest *req = data;
+ sd_resolve_query *q;
+
+ assert(resolve);
+ assert(dname);
+
+ if (resolve->dead) {
+ errno = ECHILD;
+ return NULL;
+ }
+
+ q = alloc_query(resolve);
+ if (!q)
+ return NULL;
+
+ req->dname_len = strlen(dname) + 1;
+
+ req->header.id = q->id;
+ req->header.type = q->type = qtype;
+ req->header.length = sizeof(ResRequest) + req->dname_len;
+
+ if (req->header.length > BUFSIZE) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ req->class = class;
+ req->type = type;
+
+ strcpy((char*) req + sizeof(ResRequest), dname);
+
+ if (send(resolve->fds[REQUEST_SEND_FD], req, req->header.length, MSG_NOSIGNAL) < 0)
+ goto fail;
+
+ return q;
+
+fail:
+ if (q)
+ sd_resolve_cancel(resolve, q);
+
+ return NULL;
+}
+
+_public_ sd_resolve_query* sd_resolve_res_query(sd_resolve *resolve, const char *dname, int class, int type) {
+ return resolve_res(resolve, REQUEST_RES_QUERY, dname, class, type);
+}
+
+_public_ sd_resolve_query* sd_resolve_res_search(sd_resolve *resolve, const char *dname, int class, int type) {
+ return resolve_res(resolve, REQUEST_RES_SEARCH, dname, class, type);
+}
+
+_public_ int sd_resolve_res_done(sd_resolve *resolve, sd_resolve_query* q, unsigned char **answer) {
+ int ret;
+ assert(resolve);
+ assert(q);
+ assert(q->resolve == resolve);
+ assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH);
+ assert(answer);
+
+ if (resolve->dead) {
+ errno = ECHILD;
+ return -ECHILD;
+ }
+
+ if (!q->done) {
+ errno = EAGAIN;
+ return -EAGAIN;
+ }
+
+ *answer = (unsigned char *)q->serv;
+ q->serv = NULL;
+
+ ret = q->ret;
+
+ if (ret < 0) {
+ errno = q->_errno;
+ h_errno = q->_h_errno;
+ }
+
+ sd_resolve_cancel(resolve, q);
+
+ return ret < 0 ? -errno : ret;
+}
+
+_public_ sd_resolve_query* sd_resolve_get_next(sd_resolve *resolve) {
+ assert(resolve);
+ return resolve->done_head;
+}
+
+_public_ int sd_resolve_get_n_queries(sd_resolve *resolve) {
+ assert(resolve);
+ return resolve->n_queries;
+}
+
+_public_ void sd_resolve_cancel(sd_resolve *resolve, sd_resolve_query* q) {
+ int i;
+ int saved_errno = errno;
+
+ assert(resolve);
+ assert(q);
+ assert(q->resolve == resolve);
+ assert(resolve->n_queries > 0);
+
+ if (q->done) {
+
+ if (q->done_prev)
+ q->done_prev->done_next = q->done_next;
+ else
+ resolve->done_head = q->done_next;
+
+ if (q->done_next)
+ q->done_next->done_prev = q->done_prev;
+ else
+ resolve->done_tail = q->done_prev;
+ }
+
+ i = q->id % MAX_QUERIES;
+ assert(resolve->queries[i] == q);
+ resolve->queries[i] = NULL;
+
+ sd_resolve_freeaddrinfo(q->addrinfo);
+ free(q->host);
+ free(q->serv);
+
+ resolve->n_queries--;
+ free(q);
+
+ errno = saved_errno;
+}
+
+_public_ void sd_resolve_freeaddrinfo(struct addrinfo *ai) {
+ int saved_errno = errno;
+
+ while (ai) {
+ struct addrinfo *next = ai->ai_next;
+
+ free(ai->ai_addr);
+ free(ai->ai_canonname);
+ free(ai);
+
+ ai = next;
+ }
+
+ errno = saved_errno;
+}
+
+_public_ int sd_resolve_isdone(sd_resolve *resolve, sd_resolve_query*q) {
+ assert(resolve);
+ assert(q);
+ assert(q->resolve == resolve);
+
+ return q->done;
+}
+
+_public_ void sd_resolve_setuserdata(sd_resolve *resolve, sd_resolve_query *q, void *userdata) {
+ assert(q);
+ assert(resolve);
+ assert(q->resolve = resolve);
+
+ q->userdata = userdata;
+}
+
+_public_ void* sd_resolve_getuserdata(sd_resolve *resolve, sd_resolve_query *q) {
+ assert(q);
+ assert(resolve);
+ assert(q->resolve = resolve);
+
+ return q->userdata;
+}
diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c
new file mode 100644
index 0000000..3903e64
--- /dev/null
+++ b/src/libsystemd/sd-resolve/test-resolve.c
@@ -0,0 +1,160 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2005-2008 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "sd-resolve.h"
+#include "resolve-util.h"
+#include "macro.h"
+
+int main(int argc, char *argv[]) {
+ int r = 1, ret;
+ _cleanup_resolve_free_ sd_resolve *resolve = NULL;
+ _cleanup_resolve_addrinfo_free_ struct addrinfo *ai = NULL;
+ _cleanup_free_ unsigned char *srv = NULL;
+ sd_resolve_query *q1, *q2, *q3;
+ struct addrinfo hints = {};
+ struct sockaddr_in sa = {};
+ char host[NI_MAXHOST] = "", serv[NI_MAXSERV] = "";
+
+ signal(SIGCHLD, SIG_IGN);
+
+ resolve = sd_resolve_new(2);
+ if (!resolve)
+ log_oom();
+
+ /* Make a name -> address query */
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ q1 = sd_resolve_getaddrinfo(resolve, argc >= 2 ? argv[1] : "www.heise.de", NULL, &hints);
+ if (!q1)
+ fprintf(stderr, "sd_resolve_getaddrinfo(): %s\n", strerror(errno));
+
+ /* Make an address -> name query */
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = inet_addr(argc >= 3 ? argv[2] : "193.99.144.71");
+ sa.sin_port = htons(80);
+
+ q2 = sd_resolve_getnameinfo(resolve, (struct sockaddr*) &sa, sizeof(sa), 0, 1, 1);
+ if (!q2)
+ fprintf(stderr, "sd_resolve_getnameinfo(): %s\n", strerror(errno));
+
+ /* Make a res_query() call */
+ q3 = sd_resolve_res_query(resolve, "_xmpp-client._tcp.gmail.com", C_IN, T_SRV);
+ if (!q3)
+ fprintf(stderr, "sd_resolve_res_query(): %s\n", strerror(errno));
+
+ /* Wait until the three queries are completed */
+ while (!sd_resolve_isdone(resolve, q1) ||
+ !sd_resolve_isdone(resolve, q2) ||
+ !sd_resolve_isdone(resolve, q3)) {
+ if (sd_resolve_wait(resolve, 1) < 0)
+ fprintf(stderr, "sd_resolve_wait(): %s\n", strerror(errno));
+ }
+
+ /* Interpret the result of the name -> addr query */
+ ret = sd_resolve_getaddrinfo_done(resolve, q1, &ai);
+ if (ret)
+ fprintf(stderr, "error: %s %i\n", gai_strerror(ret), ret);
+ else {
+ struct addrinfo *i;
+
+ for (i = ai; i; i = i->ai_next) {
+ char t[256];
+ const char *p = NULL;
+
+ if (i->ai_family == PF_INET)
+ p = inet_ntop(AF_INET, &((struct sockaddr_in*) i->ai_addr)->sin_addr, t, sizeof(t));
+ else if (i->ai_family == PF_INET6)
+ p = inet_ntop(AF_INET6, &((struct sockaddr_in6*) i->ai_addr)->sin6_addr, t, sizeof(t));
+
+ printf("%s\n", p);
+ }
+ }
+
+ /* Interpret the result of the addr -> name query */
+ ret = sd_resolve_getnameinfo_done(resolve, q2, host, sizeof(host), serv, sizeof(serv));
+ if (ret)
+ fprintf(stderr, "error: %s %i\n", gai_strerror(ret), ret);
+ else
+ printf("%s -- %s\n", host, serv);
+
+ /* Interpret the result of the SRV lookup */
+ ret = sd_resolve_res_done(resolve, q3, &srv);
+ if (ret < 0) {
+ fprintf(stderr, "error: %s %i\n", strerror(errno), ret);
+ } else if (ret == 0) {
+ fprintf(stderr, "No reply for SRV lookup\n");
+ } else {
+ int qdcount;
+ int ancount;
+ int len;
+ const unsigned char *pos = srv + sizeof(HEADER);
+ unsigned char *end = srv + ret;
+ HEADER *head = (HEADER *)srv;
+ char name[256];
+
+ qdcount = ntohs(head->qdcount);
+ ancount = ntohs(head->ancount);
+
+ printf("%d answers for srv lookup:\n", ancount);
+
+ /* Ignore the questions */
+ while (qdcount-- > 0 && (len = dn_expand(srv, end, pos, name, 255)) >= 0) {
+ assert(len >= 0);
+ pos += len + QFIXEDSZ;
+ }
+
+ /* Parse the answers */
+ while (ancount-- > 0 && (len = dn_expand(srv, end, pos, name, 255)) >= 0) {
+ /* Ignore the initial string */
+ uint16_t pref, weight, port;
+ assert(len >= 0);
+ pos += len;
+ /* Ignore type, ttl, class and dlen */
+ pos += 10;
+
+ GETSHORT(pref, pos);
+ GETSHORT(weight, pos);
+ GETSHORT(port, pos);
+ len = dn_expand(srv, end, pos, name, 255);
+ printf("\tpreference: %2d weight: %2d port: %d host: %s\n",
+ pref, weight, port, name);
+
+ pos += len;
+ }
+ }
+
+ r = 0;
+
+ return r;
+}
diff --git a/src/libsystemd/sd-rtnl.c b/src/libsystemd/sd-rtnl.c
deleted file mode 100644
index 08b82ab..0000000
--- a/src/libsystemd/sd-rtnl.c
+++ /dev/null
@@ -1,870 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Tom Gundersen <teg at jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/socket.h>
-#include <poll.h>
-
-#include "macro.h"
-#include "util.h"
-#include "hashmap.h"
-
-#include "sd-rtnl.h"
-#include "rtnl-internal.h"
-#include "rtnl-util.h"
-
-static int sd_rtnl_new(sd_rtnl **ret) {
- sd_rtnl *rtnl;
-
- assert_return(ret, -EINVAL);
-
- rtnl = new0(sd_rtnl, 1);
- if (!rtnl)
- return -ENOMEM;
-
- rtnl->n_ref = REFCNT_INIT;
-
- rtnl->fd = -1;
-
- rtnl->sockaddr.nl.nl_family = AF_NETLINK;
-
- rtnl->original_pid = getpid();
-
- LIST_HEAD_INIT(rtnl->match_callbacks);
-
- /* We guarantee that wqueue always has space for at least
- * one entry */
- rtnl->wqueue = new(sd_rtnl_message*, 1);
- if (!rtnl->wqueue) {
- free(rtnl);
- return -ENOMEM;
- }
-
- *ret = rtnl;
- return 0;
-}
-
-static bool rtnl_pid_changed(sd_rtnl *rtnl) {
- assert(rtnl);
-
- /* We don't support people creating an rtnl connection and
- * keeping it around over a fork(). Let's complain. */
-
- return rtnl->original_pid != getpid();
-}
-
-int sd_rtnl_open(uint32_t groups, sd_rtnl **ret) {
- _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
- socklen_t addrlen;
- int r;
-
- assert_return(ret, -EINVAL);
-
- r = sd_rtnl_new(&rtnl);
- if (r < 0)
- return r;
-
- rtnl->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
- if (rtnl->fd < 0)
- return -errno;
-
- rtnl->sockaddr.nl.nl_groups = groups;
-
- addrlen = sizeof(rtnl->sockaddr);
-
- r = bind(rtnl->fd, &rtnl->sockaddr.sa, addrlen);
- if (r < 0)
- return -errno;
-
- r = getsockname(rtnl->fd, &rtnl->sockaddr.sa, &addrlen);
- if (r < 0)
- return r;
-
- *ret = rtnl;
- rtnl = NULL;
-
- return 0;
-}
-
-sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
- if (rtnl)
- assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
-
- return rtnl;
-}
-
-sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
-
- if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) {
- struct match_callback *f;
- unsigned i;
-
- for (i = 0; i < rtnl->rqueue_size; i++)
- sd_rtnl_message_unref(rtnl->rqueue[i]);
- free(rtnl->rqueue);
-
- for (i = 0; i < rtnl->wqueue_size; i++)
- sd_rtnl_message_unref(rtnl->wqueue[i]);
- free(rtnl->wqueue);
-
- hashmap_free_free(rtnl->reply_callbacks);
- prioq_free(rtnl->reply_callbacks_prioq);
-
- while ((f = rtnl->match_callbacks)) {
- LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f);
- free(f);
- }
-
- if (rtnl->fd >= 0)
- close_nointr_nofail(rtnl->fd);
-
- free(rtnl);
- }
-
- return NULL;
-}
-
-int sd_rtnl_send(sd_rtnl *nl,
- sd_rtnl_message *message,
- uint32_t *serial) {
- int r;
-
- assert_return(nl, -EINVAL);
- assert_return(!rtnl_pid_changed(nl), -ECHILD);
- assert_return(message, -EINVAL);
-
- r = message_seal(nl, message);
- if (r < 0)
- return r;
-
- if (nl->wqueue_size <= 0) {
- /* send directly */
- r = socket_write_message(nl, message);
- if (r < 0)
- return r;
- else if (r == 0) {
- /* nothing was sent, so let's put it on
- * the queue */
- nl->wqueue[0] = sd_rtnl_message_ref(message);
- nl->wqueue_size = 1;
- }
- } else {
- sd_rtnl_message **q;
-
- /* append to queue */
- if (nl->wqueue_size >= RTNL_WQUEUE_MAX)
- return -ENOBUFS;
-
- q = realloc(nl->wqueue, sizeof(sd_rtnl_message*) * (nl->wqueue_size + 1));
- if (!q)
- return -ENOMEM;
-
- nl->wqueue = q;
- q[nl->wqueue_size ++] = sd_rtnl_message_ref(message);
- }
-
- if (serial)
- *serial = message_get_serial(message);
-
- return 1;
-}
-
-static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
- sd_rtnl_message *z = NULL;
- int r;
-
- assert(rtnl);
- assert(message);
-
- if (rtnl->rqueue_size > 0) {
- /* Dispatch a queued message */
-
- *message = rtnl->rqueue[0];
- rtnl->rqueue_size --;
- memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_rtnl_message*) * rtnl->rqueue_size);
-
- return 1;
- }
-
- /* Try to read a new message */
- r = socket_read_message(rtnl, &z);
- if (r < 0)
- return r;
- if (r == 0)
- return 0;
-
- *message = z;
-
- return 1;
-}
-
-static int dispatch_wqueue(sd_rtnl *rtnl) {
- int r, ret = 0;
-
- assert(rtnl);
-
- while (rtnl->wqueue_size > 0) {
- r = socket_write_message(rtnl, rtnl->wqueue[0]);
- if (r < 0)
- return r;
- else if (r == 0)
- /* Didn't do anything this time */
- return ret;
- else {
- /* see equivalent in sd-bus.c */
- sd_rtnl_message_unref(rtnl->wqueue[0]);
- rtnl->wqueue_size --;
- memmove(rtnl->wqueue, rtnl->wqueue + 1, sizeof(sd_rtnl_message*) * rtnl->wqueue_size);
-
- ret = 1;
- }
- }
-
- return ret;
-}
-
-static int process_timeout(sd_rtnl *rtnl) {
- _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
- struct reply_callback *c;
- usec_t n;
- int r;
-
- assert(rtnl);
-
- c = prioq_peek(rtnl->reply_callbacks_prioq);
- if (!c)
- return 0;
-
- n = now(CLOCK_MONOTONIC);
- if (c->timeout > n)
- return 0;
-
- r = message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
- if (r < 0)
- return r;
-
- assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
- hashmap_remove(rtnl->reply_callbacks, &c->serial);
-
- r = c->callback(rtnl, m, c->userdata);
- free(c);
-
- return r < 0 ? r : 1;
-}
-
-static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
- struct reply_callback *c;
- uint64_t serial;
- int r;
-
- assert(rtnl);
- assert(m);
-
- serial = message_get_serial(m);
- c = hashmap_remove(rtnl->reply_callbacks, &serial);
- if (!c)
- return 0;
-
- if (c->timeout != 0)
- prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
-
- r = c->callback(rtnl, m, c->userdata);
- free(c);
-
- return r;
-}
-
-static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
- struct match_callback *c;
- uint16_t type;
- int r;
-
- assert(rtnl);
- assert(m);
-
- r = sd_rtnl_message_get_type(m, &type);
- if (r < 0)
- return r;
-
- LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
- if (type == c->type) {
- r = c->callback(rtnl, m, c->userdata);
- if (r != 0)
- return r;
- }
- }
-
- return 0;
-}
-
-static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
- _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
- int r;
-
- assert(rtnl);
-
- r = process_timeout(rtnl);
- if (r != 0)
- goto null_message;
-
- r = dispatch_wqueue(rtnl);
- if (r != 0)
- goto null_message;
-
- r = dispatch_rqueue(rtnl, &m);
- if (r < 0)
- return r;
- if (!m)
- goto null_message;
-
- r = process_reply(rtnl, m);
- if (r != 0)
- goto null_message;
-
- r = process_match(rtnl, m);
- if (r != 0)
- goto null_message;
-
- if (ret) {
- *ret = m;
- m = NULL;
-
- return 1;
- }
-
- return 1;
-
-null_message:
- if (r >= 0 && ret)
- *ret = NULL;
-
- return r;
-}
-
-int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
- RTNL_DONT_DESTROY(rtnl);
- int r;
-
- assert_return(rtnl, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
- assert_return(!rtnl->processing, -EBUSY);
-
- rtnl->processing = true;
- r = process_running(rtnl, ret);
- rtnl->processing = false;
-
- return r;
-}
-
-static usec_t calc_elapse(uint64_t usec) {
- if (usec == (uint64_t) -1)
- return 0;
-
- if (usec == 0)
- usec = RTNL_DEFAULT_TIMEOUT;
-
- return now(CLOCK_MONOTONIC) + usec;
-}
-
-static int rtnl_poll(sd_rtnl *rtnl, bool need_more, uint64_t timeout_usec) {
- struct pollfd p[1] = {};
- struct timespec ts;
- usec_t m = (usec_t) -1;
- int r, e;
-
- assert(rtnl);
-
- e = sd_rtnl_get_events(rtnl);
- if (e < 0)
- return e;
-
- if (need_more)
- /* Caller wants more data, and doesn't care about
- * what's been read or any other timeouts. */
- return e |= POLLIN;
- else {
- usec_t until;
- /* Caller wants to process if there is something to
- * process, but doesn't care otherwise */
-
- r = sd_rtnl_get_timeout(rtnl, &until);
- if (r < 0)
- return r;
- if (r > 0) {
- usec_t nw;
- nw = now(CLOCK_MONOTONIC);
- m = until > nw ? until - nw : 0;
- }
- }
-
- if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
- m = timeout_usec;
-
- p[0].fd = rtnl->fd;
- p[0].events = e;
-
- r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
- if (r < 0)
- return -errno;
-
- return r > 0 ? 1 : 0;
-}
-
-int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) {
- assert_return(nl, -EINVAL);
- assert_return(!rtnl_pid_changed(nl), -ECHILD);
-
- if (nl->rqueue_size > 0)
- return 0;
-
- return rtnl_poll(nl, false, timeout_usec);
-}
-
-static int timeout_compare(const void *a, const void *b) {
- const struct reply_callback *x = a, *y = b;
-
- if (x->timeout != 0 && y->timeout == 0)
- return -1;
-
- if (x->timeout == 0 && y->timeout != 0)
- return 1;
-
- if (x->timeout < y->timeout)
- return -1;
-
- if (x->timeout > y->timeout)
- return 1;
-
- return 0;
-}
-
-int sd_rtnl_call_async(sd_rtnl *nl,
- sd_rtnl_message *m,
- sd_rtnl_message_handler_t callback,
- void *userdata,
- uint64_t usec,
- uint32_t *serial) {
- struct reply_callback *c;
- uint32_t s;
- int r, k;
-
- assert_return(nl, -EINVAL);
- assert_return(m, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!rtnl_pid_changed(nl), -ECHILD);
-
- r = hashmap_ensure_allocated(&nl->reply_callbacks, uint64_hash_func, uint64_compare_func);
- if (r < 0)
- return r;
-
- if (usec != (uint64_t) -1) {
- r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
- if (r < 0)
- return r;
- }
-
- c = new0(struct reply_callback, 1);
- if (!c)
- return -ENOMEM;
-
- c->callback = callback;
- c->userdata = userdata;
- c->timeout = calc_elapse(usec);
-
- k = sd_rtnl_send(nl, m, &s);
- if (k < 0) {
- free(c);
- return k;
- }
-
- c->serial = s;
-
- r = hashmap_put(nl->reply_callbacks, &c->serial, c);
- if (r < 0) {
- free(c);
- return r;
- }
-
- if (c->timeout != 0) {
- r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
- if (r > 0) {
- c->timeout = 0;
- sd_rtnl_call_async_cancel(nl, c->serial);
- return r;
- }
- }
-
- if (serial)
- *serial = s;
-
- return k;
-}
-
-int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial) {
- struct reply_callback *c;
- uint64_t s = serial;
-
- assert_return(nl, -EINVAL);
- assert_return(serial != 0, -EINVAL);
- assert_return(!rtnl_pid_changed(nl), -ECHILD);
-
- c = hashmap_remove(nl->reply_callbacks, &s);
- if (!c)
- return 0;
-
- if (c->timeout != 0)
- prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
-
- free(c);
- return 1;
-}
-
-int sd_rtnl_call(sd_rtnl *nl,
- sd_rtnl_message *message,
- uint64_t usec,
- sd_rtnl_message **ret) {
- usec_t timeout;
- uint32_t serial;
- bool room = false;
- int r;
-
- assert_return(nl, -EINVAL);
- assert_return(!rtnl_pid_changed(nl), -ECHILD);
- assert_return(message, -EINVAL);
-
- r = sd_rtnl_send(nl, message, &serial);
- if (r < 0)
- return r;
-
- timeout = calc_elapse(usec);
-
- for (;;) {
- usec_t left;
- _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *incoming = NULL;
-
- if (!room) {
- sd_rtnl_message **q;
-
- if (nl->rqueue_size >= RTNL_RQUEUE_MAX)
- return -ENOBUFS;
-
- /* Make sure there's room for queueing this
- * locally, before we read the message */
-
- q = realloc(nl->rqueue, (nl->rqueue_size + 1) * sizeof(sd_rtnl_message*));
- if (!q)
- return -ENOMEM;
-
- nl->rqueue = q;
- room = true;
- }
-
- r = socket_read_message(nl, &incoming);
- if (r < 0)
- return r;
- if (incoming) {
- uint32_t received_serial = message_get_serial(incoming);
-
- if (received_serial == serial) {
- r = sd_rtnl_message_get_errno(incoming);
- if (r < 0)
- return r;
-
- if (ret) {
- *ret = incoming;
- incoming = NULL;
- }
-
- return 1;
- }
-
- /* Room was allocated on the queue above */
- nl->rqueue[nl->rqueue_size ++] = incoming;
- incoming = NULL;
- room = false;
-
- /* Try to read more, right away */
- continue;
- }
- if (r != 0)
- continue;
-
- if (timeout > 0) {
- usec_t n;
-
- n = now(CLOCK_MONOTONIC);
- if (n >= timeout)
- return -ETIMEDOUT;
-
- left = timeout - n;
- } else
- left = (uint64_t) -1;
-
- r = rtnl_poll(nl, true, left);
- if (r < 0)
- return r;
-
- r = dispatch_wqueue(nl);
- if (r < 0)
- return r;
- }
-}
-
-int sd_rtnl_flush(sd_rtnl *rtnl) {
- int r;
-
- assert_return(rtnl, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
-
- if (rtnl->wqueue_size <= 0)
- return 0;
-
- for (;;) {
- r = dispatch_wqueue(rtnl);
- if (r < 0)
- return r;
-
- if (rtnl->wqueue_size <= 0)
- return 0;
-
- r = rtnl_poll(rtnl, false, (uint64_t) -1);
- if (r < 0)
- return r;
- }
-}
-
-int sd_rtnl_get_events(sd_rtnl *rtnl) {
- int flags = 0;
-
- assert_return(rtnl, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
-
- if (rtnl->rqueue_size <= 0)
- flags |= POLLIN;
- if (rtnl->wqueue_size > 0)
- flags |= POLLOUT;
-
- return flags;
-}
-
-int sd_rtnl_get_timeout(sd_rtnl *rtnl, uint64_t *timeout_usec) {
- struct reply_callback *c;
-
- assert_return(rtnl, -EINVAL);
- assert_return(timeout_usec, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
-
- if (rtnl->rqueue_size > 0) {
- *timeout_usec = 0;
- return 1;
- }
-
- c = prioq_peek(rtnl->reply_callbacks_prioq);
- if (!c) {
- *timeout_usec = (uint64_t) -1;
- return 0;
- }
-
- *timeout_usec = c->timeout;
-
- return 1;
-}
-
-static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- sd_rtnl *rtnl = userdata;
- int r;
-
- assert(rtnl);
-
- r = sd_rtnl_process(rtnl, NULL);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_rtnl *rtnl = userdata;
- int r;
-
- assert(rtnl);
-
- r = sd_rtnl_process(rtnl, NULL);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int prepare_callback(sd_event_source *s, void *userdata) {
- sd_rtnl *rtnl = userdata;
- int r, e;
- usec_t until;
-
- assert(s);
- assert(rtnl);
-
- e = sd_rtnl_get_events(rtnl);
- if (e < 0)
- return e;
-
- r = sd_event_source_set_io_events(rtnl->io_event_source, e);
- if (r < 0)
- return r;
-
- r = sd_rtnl_get_timeout(rtnl, &until);
- if (r < 0)
- return r;
- if (r > 0) {
- int j;
-
- j = sd_event_source_set_time(rtnl->time_event_source, until);
- if (j < 0)
- return j;
- }
-
- r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int exit_callback(sd_event_source *event, void *userdata) {
- sd_rtnl *rtnl = userdata;
-
- assert(event);
-
- sd_rtnl_flush(rtnl);
-
- return 1;
-}
-
-int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
- int r;
-
- assert_return(rtnl, -EINVAL);
- assert_return(!rtnl->event, -EBUSY);
-
- assert(!rtnl->io_event_source);
- assert(!rtnl->time_event_source);
-
- if (event)
- rtnl->event = sd_event_ref(event);
- else {
- r = sd_event_default(&rtnl->event);
- if (r < 0)
- return r;
- }
-
- r = sd_event_add_io(rtnl->event, rtnl->fd, 0, io_callback, rtnl, &rtnl->io_event_source);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_priority(rtnl->io_event_source, priority);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
- if (r < 0)
- goto fail;
-
- r = sd_event_add_monotonic(rtnl->event, 0, 0, time_callback, rtnl, &rtnl->time_event_source);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_priority(rtnl->time_event_source, priority);
- if (r < 0)
- goto fail;
-
- r = sd_event_add_exit(rtnl->event, exit_callback, rtnl, &rtnl->exit_event_source);
- if (r < 0)
- goto fail;
-
- return 0;
-
-fail:
- sd_rtnl_detach_event(rtnl);
- return r;
-}
-
-int sd_rtnl_detach_event(sd_rtnl *rtnl) {
- assert_return(rtnl, -EINVAL);
- assert_return(rtnl->event, -ENXIO);
-
- if (rtnl->io_event_source)
- rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
-
- if (rtnl->time_event_source)
- rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
-
- if (rtnl->exit_event_source)
- rtnl->exit_event_source = sd_event_source_unref(rtnl->exit_event_source);
-
- if (rtnl->event)
- rtnl->event = sd_event_unref(rtnl->event);
-
- return 0;
-}
-
-int sd_rtnl_add_match(sd_rtnl *rtnl,
- uint16_t type,
- sd_rtnl_message_handler_t callback,
- void *userdata) {
- struct match_callback *c;
-
- assert_return(rtnl, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
- assert_return(message_type_is_link(type) || message_type_is_addr(type) || message_type_is_route(type), -ENOTSUP);
-
- c = new0(struct match_callback, 1);
- if (!c)
- return -ENOMEM;
-
- c->callback = callback;
- c->type = type;
- c->userdata = userdata;
-
- LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
-
- return 0;
-}
-
-int sd_rtnl_remove_match(sd_rtnl *rtnl,
- uint16_t type,
- sd_rtnl_message_handler_t callback,
- void *userdata) {
- struct match_callback *c;
-
- assert_return(rtnl, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
-
- LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
- if (c->callback == callback && c->type == type && c->userdata == userdata) {
- LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);
- free(c);
-
- return 1;
- }
-
- return 0;
-}
diff --git a/src/libsystemd/sd-rtnl/Makefile b/src/libsystemd/sd-rtnl/Makefile
new file mode 120000
index 0000000..94aaae2
--- /dev/null
+++ b/src/libsystemd/sd-rtnl/Makefile
@@ -0,0 +1 @@
+../../Makefile
\ No newline at end of file
diff --git a/src/libsystemd/sd-rtnl/rtnl-internal.h b/src/libsystemd/sd-rtnl/rtnl-internal.h
new file mode 100644
index 0000000..2e0b7b8
--- /dev/null
+++ b/src/libsystemd/sd-rtnl/rtnl-internal.h
@@ -0,0 +1,99 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Tom Gundersen <teg at jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/netlink.h>
+
+#include "refcnt.h"
+#include "prioq.h"
+#include "list.h"
+
+#include "sd-rtnl.h"
+
+struct reply_callback {
+ sd_rtnl_message_handler_t callback;
+ void *userdata;
+ usec_t timeout;
+ uint64_t serial;
+ unsigned prioq_idx;
+};
+
+struct match_callback {
+ sd_rtnl_message_handler_t callback;
+ uint16_t type;
+ void *userdata;
+
+ LIST_FIELDS(struct match_callback, match_callbacks);
+};
+
+struct sd_rtnl {
+ RefCount n_ref;
+
+ int fd;
+
+ union {
+ struct sockaddr sa;
+ struct sockaddr_nl nl;
+ } sockaddr;
+
+ sd_rtnl_message **rqueue;
+ unsigned rqueue_size;
+
+ sd_rtnl_message **wqueue;
+ unsigned wqueue_size;
+
+ bool processing:1;
+
+ uint32_t serial;
+
+ struct Prioq *reply_callbacks_prioq;
+ Hashmap *reply_callbacks;
+
+ LIST_HEAD(struct match_callback, match_callbacks);
+
+ pid_t original_pid;
+
+ sd_event_source *io_event_source;
+ sd_event_source *time_event_source;
+ sd_event_source *exit_event_source;
+ sd_event *event;
+};
+
+#define RTNL_DEFAULT_TIMEOUT ((usec_t) (10 * USEC_PER_SEC))
+
+#define RTNL_WQUEUE_MAX 1024
+#define RTNL_RQUEUE_MAX 64*1024
+
+int message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message **ret);
+uint32_t message_get_serial(sd_rtnl_message *m);
+int message_seal(sd_rtnl *nl, sd_rtnl_message *m);
+
+bool message_type_is_link(uint16_t type);
+bool message_type_is_addr(uint16_t type);
+bool message_type_is_route(uint16_t type);
+
+int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m);
+int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret);
+
+/* Make sure callbacks don't destroy the rtnl connection */
+#define RTNL_DONT_DESTROY(rtnl) \
+ _cleanup_sd_rtnl_unref_ _unused_ sd_rtnl *_dont_destroy_##rtnl = sd_rtnl_ref(rtnl)
diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c
new file mode 100644
index 0000000..984115c
--- /dev/null
+++ b/src/libsystemd/sd-rtnl/rtnl-message.c
@@ -0,0 +1,887 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Tom Gundersen <teg at jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/rtnetlink.h>
+#include <netinet/in.h>
+#include <netinet/ether.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "refcnt.h"
+
+#include "sd-rtnl.h"
+#include "rtnl-internal.h"
+
+struct sd_rtnl_message {
+ RefCount n_ref;
+
+ struct nlmsghdr *hdr;
+ size_t container_offset; /* offset from hdr to container start */
+ size_t next_rta_offset; /* offset from hdr to next rta */
+
+ bool sealed:1;
+};
+
+#define CURRENT_CONTAINER(m) ((m)->container_offset ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->container_offset) : NULL)
+#define NEXT_RTA(m) ((struct rtattr*)((uint8_t*)(m)->hdr + (m)->next_rta_offset))
+#define UPDATE_RTA(m, new) (m)->next_rta_offset = (uint8_t*)(new) - (uint8_t*)(m)->hdr;
+
+static int message_new(sd_rtnl_message **ret, size_t initial_size) {
+ sd_rtnl_message *m;
+
+ assert_return(ret, -EINVAL);
+ assert_return(initial_size >= sizeof(struct nlmsghdr), -EINVAL);
+
+ m = new0(sd_rtnl_message, 1);
+ if (!m)
+ return -ENOMEM;
+
+ m->hdr = malloc0(initial_size);
+ if (!m->hdr) {
+ free(m);
+ return -ENOMEM;
+ }
+
+ m->n_ref = REFCNT_INIT;
+
+ m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ m->sealed = false;
+
+ *ret = m;
+
+ return 0;
+}
+
+int message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message **ret) {
+ struct nlmsgerr *err;
+ int r;
+
+ assert(error <= 0);
+
+ r = message_new(ret, NLMSG_SPACE(sizeof(struct nlmsgerr)));
+ if (r < 0)
+ return r;
+
+ (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
+ (*ret)->hdr->nlmsg_type = NLMSG_ERROR;
+ (*ret)->hdr->nlmsg_seq = serial;
+
+ err = NLMSG_DATA((*ret)->hdr);
+
+ err->error = error;
+
+ return 0;
+}
+
+bool message_type_is_route(uint16_t type) {
+ switch (type) {
+ case RTM_NEWROUTE:
+ case RTM_GETROUTE:
+ case RTM_DELROUTE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool message_type_is_link(uint16_t type) {
+ switch (type) {
+ case RTM_NEWLINK:
+ case RTM_SETLINK:
+ case RTM_GETLINK:
+ case RTM_DELLINK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool message_type_is_addr(uint16_t type) {
+ switch (type) {
+ case RTM_NEWADDR:
+ case RTM_GETADDR:
+ case RTM_DELADDR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+int sd_rtnl_message_route_set_dst_prefixlen(sd_rtnl_message *m, unsigned char prefixlen) {
+ struct rtmsg *rtm;
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ rtm->rtm_dst_len = prefixlen;
+
+ return 0;
+}
+
+int sd_rtnl_message_route_new(uint16_t nlmsg_type, unsigned char rtm_family,
+ sd_rtnl_message **ret) {
+ struct rtmsg *rtm;
+ int r;
+
+ assert_return(message_type_is_route(nlmsg_type), -EINVAL);
+ assert_return(rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = message_new(ret, NLMSG_SPACE(sizeof(struct rtmsg)));
+ if (r < 0)
+ return r;
+
+ (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ (*ret)->hdr->nlmsg_type = nlmsg_type;
+ if (nlmsg_type == RTM_NEWROUTE)
+ (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
+
+ rtm = NLMSG_DATA((*ret)->hdr);
+
+ UPDATE_RTA(*ret, RTM_RTA(rtm));
+
+ rtm->rtm_family = rtm_family;
+ rtm->rtm_scope = RT_SCOPE_UNIVERSE;
+ rtm->rtm_type = RTN_UNICAST;
+ rtm->rtm_table = RT_TABLE_MAIN;
+ rtm->rtm_protocol = RTPROT_BOOT;
+
+ return 0;
+}
+
+int sd_rtnl_message_link_set_flags(sd_rtnl_message *m, unsigned flags) {
+ struct ifinfomsg *ifi;
+
+ ifi = NLMSG_DATA(m->hdr);
+
+ ifi->ifi_flags = flags;
+ ifi->ifi_change = 0xffffffff;
+
+ return 0;
+}
+
+int sd_rtnl_message_link_set_type(sd_rtnl_message *m, unsigned type) {
+ struct ifinfomsg *ifi;
+
+ ifi = NLMSG_DATA(m->hdr);
+
+ ifi->ifi_type = type;
+
+ return 0;
+}
+
+int sd_rtnl_message_link_new(uint16_t nlmsg_type, int index, sd_rtnl_message **ret) {
+ struct ifinfomsg *ifi;
+ int r;
+
+ assert_return(message_type_is_link(nlmsg_type), -EINVAL);
+ assert_return(nlmsg_type == RTM_NEWLINK || index > 0, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = message_new(ret, NLMSG_SPACE(sizeof(struct ifinfomsg)));
+ if (r < 0)
+ return r;
+
+ (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ (*ret)->hdr->nlmsg_type = nlmsg_type;
+ if (nlmsg_type == RTM_NEWLINK)
+ (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE;
+
+ ifi = NLMSG_DATA((*ret)->hdr);
+
+ ifi->ifi_family = AF_UNSPEC;
+ ifi->ifi_index = index;
+
+ UPDATE_RTA(*ret, IFLA_RTA(ifi));
+
+ return 0;
+}
+
+int sd_rtnl_message_addr_new(uint16_t nlmsg_type, int index, unsigned char family, unsigned char prefixlen, unsigned char flags, unsigned char scope, sd_rtnl_message **ret) {
+ struct ifaddrmsg *ifa;
+ int r;
+
+ assert_return(message_type_is_addr(nlmsg_type), -EINVAL);
+ assert_return(index > 0, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = message_new(ret, NLMSG_SPACE(sizeof(struct ifaddrmsg)));
+ if (r < 0)
+ return r;
+
+ (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ (*ret)->hdr->nlmsg_type = nlmsg_type;
+
+ ifa = NLMSG_DATA((*ret)->hdr);
+
+ ifa->ifa_family = family;
+ ifa->ifa_prefixlen = prefixlen;
+ ifa->ifa_flags = flags;
+ ifa->ifa_scope = scope;
+ ifa->ifa_index = index;
+
+ UPDATE_RTA(*ret, IFA_RTA(ifa));
+
+ return 0;
+}
+
+sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
+ if (m)
+ assert_se(REFCNT_INC(m->n_ref) >= 2);
+
+ return m;
+}
+
+sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) {
+ if (m && REFCNT_DEC(m->n_ref) <= 0) {
+ free(m->hdr);
+ free(m);
+ }
+
+ return NULL;
+}
+
+int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type) {
+ assert_return(m, -EINVAL);
+ assert_return(type, -EINVAL);
+
+ *type = m->hdr->nlmsg_type;
+
+ return 0;
+}
+
+int sd_rtnl_message_link_get_ifindex(sd_rtnl_message *m, int *ifindex) {
+ struct ifinfomsg *ifi;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
+ assert_return(ifindex, -EINVAL);
+
+ ifi = NLMSG_DATA(m->hdr);
+
+ *ifindex = ifi->ifi_index;
+
+ return 0;
+}
+
+int sd_rtnl_message_link_get_flags(sd_rtnl_message *m, unsigned *flags) {
+ struct ifinfomsg *ifi;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
+ assert_return(flags, -EINVAL);
+
+ ifi = NLMSG_DATA(m->hdr);
+
+ *flags = ifi->ifi_flags;
+
+ return 0;
+}
+
+/* If successful the updated message will be correctly aligned, if
+ unsuccessful the old message is untouched. */
+static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, size_t data_length) {
+ uint32_t rta_length, message_length;
+ struct nlmsghdr *new_hdr;
+ struct rtattr *rta;
+ char *padding;
+
+ assert(m);
+ assert(m->hdr);
+ assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len);
+ assert(!data || data_length > 0);
+
+ /* get the size of the new rta attribute (with padding at the end) */
+ rta_length = RTA_LENGTH(data_length);
+
+ /* get the new message size (with padding at the end) */
+ message_length = m->hdr->nlmsg_len + RTA_ALIGN(rta_length);
+
+ /* realloc to fit the new attribute */
+ new_hdr = realloc(m->hdr, message_length);
+ if (!new_hdr)
+ return -ENOMEM;
+ m->hdr = new_hdr;
+
+ /* get pointer to the attribute we are about to add */
+ rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
+
+ /* if we are inside a container, extend it */
+ if (CURRENT_CONTAINER(m))
+ CURRENT_CONTAINER(m)->rta_len += message_length - m->hdr->nlmsg_len;
+
+ /* fill in the attribute */
+ rta->rta_type = type;
+ rta->rta_len = rta_length;
+ if (!data) {
+ /* this is the start of a new container */
+ m->container_offset = m->hdr->nlmsg_len;
+ } else {
+ /* we don't deal with the case where the user lies about the type
+ * and gives us too little data (so don't do that)
+ */
+ padding = mempcpy(RTA_DATA(rta), data, data_length);
+ /* make sure also the padding at the end of the message is initialized */
+ memzero(padding,
+ (uint8_t *) m->hdr + message_length - (uint8_t *) padding);
+ }
+
+ /* update message size */
+ m->hdr->nlmsg_len = message_length;
+
+ return 0;
+}
+
+int sd_rtnl_message_append_string(sd_rtnl_message *m, unsigned short type, const char *data) {
+ uint16_t rtm_type;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(data, -EINVAL);
+
+ r = sd_rtnl_message_get_type(m, &rtm_type);
+ if (r < 0)
+ return r;
+
+ /* check that the type is correct */
+ switch (rtm_type) {
+ case RTM_NEWLINK:
+ case RTM_SETLINK:
+ case RTM_GETLINK:
+ case RTM_DELLINK:
+ if (CURRENT_CONTAINER(m)) {
+ if (CURRENT_CONTAINER(m)->rta_type != IFLA_LINKINFO ||
+ type != IFLA_INFO_KIND)
+ return -ENOTSUP;
+ } else {
+ switch (type) {
+ case IFLA_IFNAME:
+ case IFLA_IFALIAS:
+ case IFLA_QDISC:
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ }
+ break;
+ case RTM_NEWADDR:
+ case RTM_GETADDR:
+ case RTM_DELADDR:
+ if (type != IFA_LABEL)
+ return -ENOTSUP;
+ break;
+ default:
+ return -ENOTSUP;
+ }
+
+ r = add_rtattr(m, type, data, strlen(data) + 1);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sd_rtnl_message_append_u32(sd_rtnl_message *m, unsigned short type, uint32_t data) {
+ uint16_t rtm_type;
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ r = sd_rtnl_message_get_type(m, &rtm_type);
+ if (r < 0)
+ return r;
+
+ /* check that the type is correct */
+ switch (rtm_type) {
+ case RTM_NEWLINK:
+ case RTM_SETLINK:
+ case RTM_GETLINK:
+ case RTM_DELLINK:
+ switch (type) {
+ case IFLA_MASTER:
+ case IFLA_MTU:
+ case IFLA_LINK:
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ break;
+ case RTM_NEWROUTE:
+ case RTM_GETROUTE:
+ case RTM_DELROUTE:
+ switch (type) {
+ case RTA_TABLE:
+ case RTA_PRIORITY:
+ case RTA_IIF:
+ case RTA_OIF:
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ break;
+ default:
+ return -ENOTSUP;
+ }
+
+ r = add_rtattr(m, type, &data, sizeof(uint32_t));
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sd_rtnl_message_append_in_addr(sd_rtnl_message *m, unsigned short type, const struct in_addr *data) {
+ struct ifaddrmsg *ifa;
+ struct rtmsg *rtm;
+ uint16_t rtm_type;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(data, -EINVAL);
+
+ r = sd_rtnl_message_get_type(m, &rtm_type);
+ if (r < 0)
+ return r;
+
+ /* check that the type is correct */
+ switch (rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_GETADDR:
+ case RTM_DELADDR:
+ switch (type) {
+ case IFA_ADDRESS:
+ case IFA_LOCAL:
+ case IFA_BROADCAST:
+ case IFA_ANYCAST:
+ ifa = NLMSG_DATA(m->hdr);
+
+ if (ifa->ifa_family != AF_INET)
+ return -EINVAL;
+
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ break;
+ case RTM_NEWROUTE:
+ case RTM_GETROUTE:
+ case RTM_DELROUTE:
+ switch (type) {
+ case RTA_DST:
+ case RTA_SRC:
+ case RTA_GATEWAY:
+ rtm = NLMSG_DATA(m->hdr);
+
+ if (rtm->rtm_family != AF_INET)
+ return -EINVAL;
+
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ break;
+ default:
+ return -ENOTSUP;
+ }
+
+ r = add_rtattr(m, type, data, sizeof(struct in_addr));
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sd_rtnl_message_append_in6_addr(sd_rtnl_message *m, unsigned short type, const struct in6_addr *data) {
+ struct ifaddrmsg *ifa;
+ struct rtmsg *rtm;
+ uint16_t rtm_type;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(data, -EINVAL);
+
+ r = sd_rtnl_message_get_type(m, &rtm_type);
+ if (r < 0)
+ return r;
+
+ /* check that the type is correct */
+ switch (rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_GETADDR:
+ case RTM_DELADDR:
+ switch (type) {
+ case IFA_ADDRESS:
+ case IFA_LOCAL:
+ case IFA_BROADCAST:
+ case IFA_ANYCAST:
+ ifa = NLMSG_DATA(m->hdr);
+
+ if (ifa->ifa_family != AF_INET6)
+ return -EINVAL;
+
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ break;
+ case RTM_NEWROUTE:
+ case RTM_GETROUTE:
+ case RTM_DELROUTE:
+ switch (type) {
+ case RTA_DST:
+ case RTA_SRC:
+ case RTA_GATEWAY:
+ rtm = NLMSG_DATA(m->hdr);
+
+ if (rtm->rtm_family != AF_INET6)
+ return -EINVAL;
+
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ default:
+ return -ENOTSUP;
+ }
+
+ r = add_rtattr(m, type, data, sizeof(struct in6_addr));
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sd_rtnl_message_append_ether_addr(sd_rtnl_message *m, unsigned short type, const struct ether_addr *data) {
+ uint16_t rtm_type;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(data, -EINVAL);
+
+ sd_rtnl_message_get_type(m, &rtm_type);
+
+ switch (rtm_type) {
+ case RTM_NEWLINK:
+ case RTM_SETLINK:
+ case RTM_DELLINK:
+ case RTM_GETLINK:
+ switch (type) {
+ case IFLA_ADDRESS:
+ case IFLA_BROADCAST:
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ break;
+ default:
+ return -ENOTSUP;
+ }
+
+ r = add_rtattr(m, type, data, ETH_ALEN);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) {
+ uint16_t rtm_type;
+
+ assert_return(m, -EINVAL);
+ assert_return(!CURRENT_CONTAINER(m), -EINVAL);
+
+ sd_rtnl_message_get_type(m, &rtm_type);
+
+ if (message_type_is_link(rtm_type)) {
+ if (type == IFLA_LINKINFO)
+ return add_rtattr(m, type, NULL, 0);
+ else
+ return -ENOTSUP;
+ } else
+ return -ENOTSUP;
+
+ return 0;
+}
+
+int sd_rtnl_message_close_container(sd_rtnl_message *m) {
+ assert_return(m, -EINVAL);
+ assert_return(CURRENT_CONTAINER(m), -EINVAL);
+
+ m->container_offset = 0;
+
+ return 0;
+}
+
+int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
+ size_t remaining_size;
+ uint16_t rtm_type;
+ int r;
+
+ assert(m);
+ assert(m->next_rta_offset);
+ assert(type);
+ assert(data);
+
+ remaining_size = m->hdr->nlmsg_len - m->next_rta_offset;
+
+ if (!RTA_OK(NEXT_RTA(m), remaining_size))
+ return 0;
+
+ /* make sure we don't try to read a container
+ * TODO: add support for entering containers for reading */
+ r = sd_rtnl_message_get_type(m, &rtm_type);
+ if (r < 0)
+ return r;
+
+ if (message_type_is_link(rtm_type) &&
+ NEXT_RTA(m)->rta_type == IFLA_LINKINFO)
+ return -EINVAL;
+
+ *data = RTA_DATA(NEXT_RTA(m));
+ *type = NEXT_RTA(m)->rta_type;
+
+ UPDATE_RTA(m, RTA_NEXT(NEXT_RTA(m), remaining_size));
+
+ return 1;
+}
+
+uint32_t message_get_serial(sd_rtnl_message *m) {
+ assert(m);
+ assert(m->hdr);
+
+ return m->hdr->nlmsg_seq;
+}
+
+int sd_rtnl_message_get_errno(sd_rtnl_message *m) {
+ struct nlmsgerr *err;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+
+ if (m->hdr->nlmsg_type != NLMSG_ERROR)
+ return 0;
+
+ err = NLMSG_DATA(m->hdr);
+
+ return err->error;
+}
+
+int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
+ assert(nl);
+ assert(m);
+ assert(m->hdr);
+
+ if (m->sealed)
+ return -EPERM;
+
+ m->hdr->nlmsg_seq = nl->serial++;
+ m->sealed = true;
+
+ return 0;
+}
+
+static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
+ assert(rtnl);
+ assert(need);
+
+ /* ioctl(rtnl->fd, FIONREAD, &need)
+ Does not appear to work on netlink sockets. libnl uses
+ MSG_PEEK instead. I don't know if that is worth the
+ extra roundtrip.
+
+ For now we simply use the maximum message size the kernel
+ may use (NLMSG_GOODSIZE), and then realloc to the actual
+ size after reading the message (hence avoiding huge memory
+ usage in case many small messages are kept around) */
+ *need = page_size();
+ if (*need > 8192UL)
+ *need = 8192UL;
+
+ return 0;
+}
+
+/* returns the number of bytes sent, or a negative error code */
+int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_nl nl;
+ } addr = {
+ .nl.nl_family = AF_NETLINK,
+ };
+ ssize_t k;
+
+ assert(nl);
+ assert(m);
+ assert(m->hdr);
+
+ k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
+ 0, &addr.sa, sizeof(addr));
+ if (k < 0)
+ return (errno == EAGAIN) ? 0 : -errno;
+
+ return k;
+}
+
+/* On success, the number of bytes received is returned and *ret points to the received message
+ * which has a valid header and the correct size.
+ * If nothing useful was received 0 is returned.
+ * On failure, a negative error code is returned.
+ */
+int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
+ sd_rtnl_message *m;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_nl nl;
+ } addr;
+ socklen_t addr_len;
+ int r;
+ ssize_t k;
+ size_t need;
+
+ assert(nl);
+ assert(ret);
+
+ r = message_receive_need(nl, &need);
+ if (r < 0)
+ return r;
+
+ r = message_new(&m, need);
+ if (r < 0)
+ return r;
+
+ addr_len = sizeof(addr);
+
+ k = recvfrom(nl->fd, m->hdr, need,
+ 0, &addr.sa, &addr_len);
+ if (k < 0)
+ k = (errno == EAGAIN) ? 0 : -errno; /* no data */
+ else if (k == 0)
+ k = -ECONNRESET; /* connection was closed by the kernel */
+ else if (addr_len != sizeof(addr.nl) ||
+ addr.nl.nl_family != AF_NETLINK)
+ k = -EIO; /* not a netlink message */
+ else if (addr.nl.nl_pid != 0)
+ k = 0; /* not from the kernel */
+ else if ((size_t) k < sizeof(struct nlmsghdr) ||
+ (size_t) k < m->hdr->nlmsg_len)
+ k = -EIO; /* too small (we do accept too big though) */
+ else if (m->hdr->nlmsg_pid && m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
+ k = 0; /* not broadcast and not for us */
+
+ if (k > 0)
+ switch (m->hdr->nlmsg_type) {
+ struct ifinfomsg *ifi;
+ struct ifaddrmsg *ifa;
+ struct rtmsg *rtm;
+
+ /* check that the size matches the message type */
+ case NLMSG_ERROR:
+ if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
+ k = -EIO;
+ break;
+ case RTM_NEWLINK:
+ case RTM_SETLINK:
+ case RTM_DELLINK:
+ case RTM_GETLINK:
+ if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
+ k = -EIO;
+ else {
+ ifi = NLMSG_DATA(m->hdr);
+ UPDATE_RTA(m, IFLA_RTA(ifi));
+ }
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ case RTM_GETADDR:
+ if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
+ k = -EIO;
+ else {
+ ifa = NLMSG_DATA(m->hdr);
+ UPDATE_RTA(m, IFA_RTA(ifa));
+ }
+ break;
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ case RTM_GETROUTE:
+ if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg)))
+ k = -EIO;
+ else {
+ rtm = NLMSG_DATA(m->hdr);
+ UPDATE_RTA(m, RTM_RTA(rtm));
+ }
+ break;
+ case NLMSG_NOOP:
+ k = 0;
+ break;
+ default:
+ k = 0; /* ignoring message of unknown type */
+ }
+
+ if (k <= 0)
+ sd_rtnl_message_unref(m);
+ else {
+ /* we probably allocated way too much memory, give it back */
+ m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);
+ *ret = m;
+ }
+
+ return k;
+}
+
+int sd_rtnl_message_rewind(sd_rtnl_message *m) {
+ struct ifinfomsg *ifi;
+ struct ifaddrmsg *ifa;
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+
+ switch(m->hdr->nlmsg_type) {
+ case RTM_NEWLINK:
+ case RTM_SETLINK:
+ case RTM_GETLINK:
+ case RTM_DELLINK:
+ ifi = NLMSG_DATA(m->hdr);
+ UPDATE_RTA(m, IFLA_RTA(ifi));
+
+ break;
+ case RTM_NEWADDR:
+ case RTM_GETADDR:
+ case RTM_DELADDR:
+ ifa = NLMSG_DATA(m->hdr);
+ UPDATE_RTA(m, IFA_RTA(ifa));
+
+ break;
+ case RTM_NEWROUTE:
+ case RTM_GETROUTE:
+ case RTM_DELROUTE:
+ rtm = NLMSG_DATA(m->hdr);
+ UPDATE_RTA(m, RTM_RTA(rtm));
+
+ break;
+ default:
+ return -ENOTSUP;
+ }
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-rtnl/rtnl-util.c b/src/libsystemd/sd-rtnl/rtnl-util.c
new file mode 100644
index 0000000..dfc0050
--- /dev/null
+++ b/src/libsystemd/sd-rtnl/rtnl-util.c
@@ -0,0 +1,100 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2013 Tom Gundersen <teg at jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/rtnetlink.h>
+#include <netinet/ether.h>
+
+#include "sd-rtnl.h"
+
+#include "rtnl-util.h"
+
+int rtnl_set_link_name(sd_rtnl *rtnl, int ifindex, const char *name) {
+ _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message = NULL;
+ int r;
+
+ assert(rtnl);
+ assert(ifindex > 0);
+ assert(name);
+
+ r = sd_rtnl_message_link_new(RTM_SETLINK, ifindex, &message);
+ if (r < 0)
+ return r;
+
+ r = sd_rtnl_message_append_string(message, IFLA_IFNAME, name);
+ if (r < 0)
+ return r;
+
+ r = sd_rtnl_call(rtnl, message, 0, NULL);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int rtnl_set_link_properties(sd_rtnl *rtnl, int ifindex, const char *alias,
+ const struct ether_addr *mac, unsigned mtu) {
+ _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message = NULL;
+ bool need_update = false;
+ int r;
+
+ assert(rtnl);
+ assert(ifindex > 0);
+
+ if (!alias && !mac && mtu == 0)
+ return 0;
+
+ r = sd_rtnl_message_link_new(RTM_SETLINK, ifindex, &message);
+ if (r < 0)
+ return r;
+
+ if (alias) {
+ r = sd_rtnl_message_append_string(message, IFLA_IFALIAS, alias);
+ if (r < 0)
+ return r;
+
+ need_update = true;
+
+ }
+
+ if (mac) {
+ r = sd_rtnl_message_append_ether_addr(message, IFLA_ADDRESS, mac);
+ if (r < 0)
+ return r;
+
+ need_update = true;
+ }
+
+ if (mtu > 0) {
+ r = sd_rtnl_message_append_u32(message, IFLA_MTU, mtu);
+ if (r < 0)
+ return r;
+
+ need_update = true;
+ }
+
+ if (need_update) {
+ r = sd_rtnl_call(rtnl, message, 0, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-rtnl/rtnl-util.h b/src/libsystemd/sd-rtnl/rtnl-util.h
new file mode 100644
index 0000000..013002d
--- /dev/null
+++ b/src/libsystemd/sd-rtnl/rtnl-util.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2013 Tom Gundersen <teg at jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/ether.h>
+
+#include "util.h"
+#include "sd-rtnl.h"
+
+int rtnl_set_link_name(sd_rtnl *rtnl, int ifindex, const char *name);
+int rtnl_set_link_properties(sd_rtnl *rtnl, int ifindex, const char *alias, const struct ether_addr *mac, unsigned mtu);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_rtnl*, sd_rtnl_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_rtnl_message*, sd_rtnl_message_unref);
+
+#define _cleanup_sd_rtnl_unref_ _cleanup_(sd_rtnl_unrefp)
+#define _cleanup_sd_rtnl_message_unref_ _cleanup_(sd_rtnl_message_unrefp)
diff --git a/src/libsystemd/sd-rtnl/sd-rtnl.c b/src/libsystemd/sd-rtnl/sd-rtnl.c
new file mode 100644
index 0000000..08b82ab
--- /dev/null
+++ b/src/libsystemd/sd-rtnl/sd-rtnl.c
@@ -0,0 +1,870 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Tom Gundersen <teg at jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/socket.h>
+#include <poll.h>
+
+#include "macro.h"
+#include "util.h"
+#include "hashmap.h"
+
+#include "sd-rtnl.h"
+#include "rtnl-internal.h"
+#include "rtnl-util.h"
+
+static int sd_rtnl_new(sd_rtnl **ret) {
+ sd_rtnl *rtnl;
+
+ assert_return(ret, -EINVAL);
+
+ rtnl = new0(sd_rtnl, 1);
+ if (!rtnl)
+ return -ENOMEM;
+
+ rtnl->n_ref = REFCNT_INIT;
+
+ rtnl->fd = -1;
+
+ rtnl->sockaddr.nl.nl_family = AF_NETLINK;
+
+ rtnl->original_pid = getpid();
+
+ LIST_HEAD_INIT(rtnl->match_callbacks);
+
+ /* We guarantee that wqueue always has space for at least
+ * one entry */
+ rtnl->wqueue = new(sd_rtnl_message*, 1);
+ if (!rtnl->wqueue) {
+ free(rtnl);
+ return -ENOMEM;
+ }
+
+ *ret = rtnl;
+ return 0;
+}
+
+static bool rtnl_pid_changed(sd_rtnl *rtnl) {
+ assert(rtnl);
+
+ /* We don't support people creating an rtnl connection and
+ * keeping it around over a fork(). Let's complain. */
+
+ return rtnl->original_pid != getpid();
+}
+
+int sd_rtnl_open(uint32_t groups, sd_rtnl **ret) {
+ _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
+ socklen_t addrlen;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ r = sd_rtnl_new(&rtnl);
+ if (r < 0)
+ return r;
+
+ rtnl->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
+ if (rtnl->fd < 0)
+ return -errno;
+
+ rtnl->sockaddr.nl.nl_groups = groups;
+
+ addrlen = sizeof(rtnl->sockaddr);
+
+ r = bind(rtnl->fd, &rtnl->sockaddr.sa, addrlen);
+ if (r < 0)
+ return -errno;
+
+ r = getsockname(rtnl->fd, &rtnl->sockaddr.sa, &addrlen);
+ if (r < 0)
+ return r;
+
+ *ret = rtnl;
+ rtnl = NULL;
+
+ return 0;
+}
+
+sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
+ if (rtnl)
+ assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
+
+ return rtnl;
+}
+
+sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
+
+ if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) {
+ struct match_callback *f;
+ unsigned i;
+
+ for (i = 0; i < rtnl->rqueue_size; i++)
+ sd_rtnl_message_unref(rtnl->rqueue[i]);
+ free(rtnl->rqueue);
+
+ for (i = 0; i < rtnl->wqueue_size; i++)
+ sd_rtnl_message_unref(rtnl->wqueue[i]);
+ free(rtnl->wqueue);
+
+ hashmap_free_free(rtnl->reply_callbacks);
+ prioq_free(rtnl->reply_callbacks_prioq);
+
+ while ((f = rtnl->match_callbacks)) {
+ LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f);
+ free(f);
+ }
+
+ if (rtnl->fd >= 0)
+ close_nointr_nofail(rtnl->fd);
+
+ free(rtnl);
+ }
+
+ return NULL;
+}
+
+int sd_rtnl_send(sd_rtnl *nl,
+ sd_rtnl_message *message,
+ uint32_t *serial) {
+ int r;
+
+ assert_return(nl, -EINVAL);
+ assert_return(!rtnl_pid_changed(nl), -ECHILD);
+ assert_return(message, -EINVAL);
+
+ r = message_seal(nl, message);
+ if (r < 0)
+ return r;
+
+ if (nl->wqueue_size <= 0) {
+ /* send directly */
+ r = socket_write_message(nl, message);
+ if (r < 0)
+ return r;
+ else if (r == 0) {
+ /* nothing was sent, so let's put it on
+ * the queue */
+ nl->wqueue[0] = sd_rtnl_message_ref(message);
+ nl->wqueue_size = 1;
+ }
+ } else {
+ sd_rtnl_message **q;
+
+ /* append to queue */
+ if (nl->wqueue_size >= RTNL_WQUEUE_MAX)
+ return -ENOBUFS;
+
+ q = realloc(nl->wqueue, sizeof(sd_rtnl_message*) * (nl->wqueue_size + 1));
+ if (!q)
+ return -ENOMEM;
+
+ nl->wqueue = q;
+ q[nl->wqueue_size ++] = sd_rtnl_message_ref(message);
+ }
+
+ if (serial)
+ *serial = message_get_serial(message);
+
+ return 1;
+}
+
+static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
+ sd_rtnl_message *z = NULL;
+ int r;
+
+ assert(rtnl);
+ assert(message);
+
+ if (rtnl->rqueue_size > 0) {
+ /* Dispatch a queued message */
+
+ *message = rtnl->rqueue[0];
+ rtnl->rqueue_size --;
+ memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_rtnl_message*) * rtnl->rqueue_size);
+
+ return 1;
+ }
+
+ /* Try to read a new message */
+ r = socket_read_message(rtnl, &z);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 0;
+
+ *message = z;
+
+ return 1;
+}
+
+static int dispatch_wqueue(sd_rtnl *rtnl) {
+ int r, ret = 0;
+
+ assert(rtnl);
+
+ while (rtnl->wqueue_size > 0) {
+ r = socket_write_message(rtnl, rtnl->wqueue[0]);
+ if (r < 0)
+ return r;
+ else if (r == 0)
+ /* Didn't do anything this time */
+ return ret;
+ else {
+ /* see equivalent in sd-bus.c */
+ sd_rtnl_message_unref(rtnl->wqueue[0]);
+ rtnl->wqueue_size --;
+ memmove(rtnl->wqueue, rtnl->wqueue + 1, sizeof(sd_rtnl_message*) * rtnl->wqueue_size);
+
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+static int process_timeout(sd_rtnl *rtnl) {
+ _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
+ struct reply_callback *c;
+ usec_t n;
+ int r;
+
+ assert(rtnl);
+
+ c = prioq_peek(rtnl->reply_callbacks_prioq);
+ if (!c)
+ return 0;
+
+ n = now(CLOCK_MONOTONIC);
+ if (c->timeout > n)
+ return 0;
+
+ r = message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
+ if (r < 0)
+ return r;
+
+ assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
+ hashmap_remove(rtnl->reply_callbacks, &c->serial);
+
+ r = c->callback(rtnl, m, c->userdata);
+ free(c);
+
+ return r < 0 ? r : 1;
+}
+
+static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
+ struct reply_callback *c;
+ uint64_t serial;
+ int r;
+
+ assert(rtnl);
+ assert(m);
+
+ serial = message_get_serial(m);
+ c = hashmap_remove(rtnl->reply_callbacks, &serial);
+ if (!c)
+ return 0;
+
+ if (c->timeout != 0)
+ prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
+
+ r = c->callback(rtnl, m, c->userdata);
+ free(c);
+
+ return r;
+}
+
+static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
+ struct match_callback *c;
+ uint16_t type;
+ int r;
+
+ assert(rtnl);
+ assert(m);
+
+ r = sd_rtnl_message_get_type(m, &type);
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
+ if (type == c->type) {
+ r = c->callback(rtnl, m, c->userdata);
+ if (r != 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
+ _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
+ int r;
+
+ assert(rtnl);
+
+ r = process_timeout(rtnl);
+ if (r != 0)
+ goto null_message;
+
+ r = dispatch_wqueue(rtnl);
+ if (r != 0)
+ goto null_message;
+
+ r = dispatch_rqueue(rtnl, &m);
+ if (r < 0)
+ return r;
+ if (!m)
+ goto null_message;
+
+ r = process_reply(rtnl, m);
+ if (r != 0)
+ goto null_message;
+
+ r = process_match(rtnl, m);
+ if (r != 0)
+ goto null_message;
+
+ if (ret) {
+ *ret = m;
+ m = NULL;
+
+ return 1;
+ }
+
+ return 1;
+
+null_message:
+ if (r >= 0 && ret)
+ *ret = NULL;
+
+ return r;
+}
+
+int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
+ RTNL_DONT_DESTROY(rtnl);
+ int r;
+
+ assert_return(rtnl, -EINVAL);
+ assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+ assert_return(!rtnl->processing, -EBUSY);
+
+ rtnl->processing = true;
+ r = process_running(rtnl, ret);
+ rtnl->processing = false;
+
+ return r;
+}
+
+static usec_t calc_elapse(uint64_t usec) {
+ if (usec == (uint64_t) -1)
+ return 0;
+
+ if (usec == 0)
+ usec = RTNL_DEFAULT_TIMEOUT;
+
+ return now(CLOCK_MONOTONIC) + usec;
+}
+
+static int rtnl_poll(sd_rtnl *rtnl, bool need_more, uint64_t timeout_usec) {
+ struct pollfd p[1] = {};
+ struct timespec ts;
+ usec_t m = (usec_t) -1;
+ int r, e;
+
+ assert(rtnl);
+
+ e = sd_rtnl_get_events(rtnl);
+ if (e < 0)
+ return e;
+
+ if (need_more)
+ /* Caller wants more data, and doesn't care about
+ * what's been read or any other timeouts. */
+ return e |= POLLIN;
+ else {
+ usec_t until;
+ /* Caller wants to process if there is something to
+ * process, but doesn't care otherwise */
+
+ r = sd_rtnl_get_timeout(rtnl, &until);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ usec_t nw;
+ nw = now(CLOCK_MONOTONIC);
+ m = until > nw ? until - nw : 0;
+ }
+ }
+
+ if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
+ m = timeout_usec;
+
+ p[0].fd = rtnl->fd;
+ p[0].events = e;
+
+ r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
+ if (r < 0)
+ return -errno;
+
+ return r > 0 ? 1 : 0;
+}
+
+int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) {
+ assert_return(nl, -EINVAL);
+ assert_return(!rtnl_pid_changed(nl), -ECHILD);
+
+ if (nl->rqueue_size > 0)
+ return 0;
+
+ return rtnl_poll(nl, false, timeout_usec);
+}
+
+static int timeout_compare(const void *a, const void *b) {
+ const struct reply_callback *x = a, *y = b;
+
+ if (x->timeout != 0 && y->timeout == 0)
+ return -1;
+
+ if (x->timeout == 0 && y->timeout != 0)
+ return 1;
+
+ if (x->timeout < y->timeout)
+ return -1;
+
+ if (x->timeout > y->timeout)
+ return 1;
+
+ return 0;
+}
+
+int sd_rtnl_call_async(sd_rtnl *nl,
+ sd_rtnl_message *m,
+ sd_rtnl_message_handler_t callback,
+ void *userdata,
+ uint64_t usec,
+ uint32_t *serial) {
+ struct reply_callback *c;
+ uint32_t s;
+ int r, k;
+
+ assert_return(nl, -EINVAL);
+ assert_return(m, -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(!rtnl_pid_changed(nl), -ECHILD);
+
+ r = hashmap_ensure_allocated(&nl->reply_callbacks, uint64_hash_func, uint64_compare_func);
+ if (r < 0)
+ return r;
+
+ if (usec != (uint64_t) -1) {
+ r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
+ if (r < 0)
+ return r;
+ }
+
+ c = new0(struct reply_callback, 1);
+ if (!c)
+ return -ENOMEM;
+
+ c->callback = callback;
+ c->userdata = userdata;
+ c->timeout = calc_elapse(usec);
+
+ k = sd_rtnl_send(nl, m, &s);
+ if (k < 0) {
+ free(c);
+ return k;
+ }
+
+ c->serial = s;
+
+ r = hashmap_put(nl->reply_callbacks, &c->serial, c);
+ if (r < 0) {
+ free(c);
+ return r;
+ }
+
+ if (c->timeout != 0) {
+ r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
+ if (r > 0) {
+ c->timeout = 0;
+ sd_rtnl_call_async_cancel(nl, c->serial);
+ return r;
+ }
+ }
+
+ if (serial)
+ *serial = s;
+
+ return k;
+}
+
+int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial) {
+ struct reply_callback *c;
+ uint64_t s = serial;
+
+ assert_return(nl, -EINVAL);
+ assert_return(serial != 0, -EINVAL);
+ assert_return(!rtnl_pid_changed(nl), -ECHILD);
+
+ c = hashmap_remove(nl->reply_callbacks, &s);
+ if (!c)
+ return 0;
+
+ if (c->timeout != 0)
+ prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
+
+ free(c);
+ return 1;
+}
+
+int sd_rtnl_call(sd_rtnl *nl,
+ sd_rtnl_message *message,
+ uint64_t usec,
+ sd_rtnl_message **ret) {
+ usec_t timeout;
+ uint32_t serial;
+ bool room = false;
+ int r;
+
+ assert_return(nl, -EINVAL);
+ assert_return(!rtnl_pid_changed(nl), -ECHILD);
+ assert_return(message, -EINVAL);
+
+ r = sd_rtnl_send(nl, message, &serial);
+ if (r < 0)
+ return r;
+
+ timeout = calc_elapse(usec);
+
+ for (;;) {
+ usec_t left;
+ _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *incoming = NULL;
+
+ if (!room) {
+ sd_rtnl_message **q;
+
+ if (nl->rqueue_size >= RTNL_RQUEUE_MAX)
+ return -ENOBUFS;
+
+ /* Make sure there's room for queueing this
+ * locally, before we read the message */
+
+ q = realloc(nl->rqueue, (nl->rqueue_size + 1) * sizeof(sd_rtnl_message*));
+ if (!q)
+ return -ENOMEM;
+
+ nl->rqueue = q;
+ room = true;
+ }
+
+ r = socket_read_message(nl, &incoming);
+ if (r < 0)
+ return r;
+ if (incoming) {
+ uint32_t received_serial = message_get_serial(incoming);
+
+ if (received_serial == serial) {
+ r = sd_rtnl_message_get_errno(incoming);
+ if (r < 0)
+ return r;
+
+ if (ret) {
+ *ret = incoming;
+ incoming = NULL;
+ }
+
+ return 1;
+ }
+
+ /* Room was allocated on the queue above */
+ nl->rqueue[nl->rqueue_size ++] = incoming;
+ incoming = NULL;
+ room = false;
+
+ /* Try to read more, right away */
+ continue;
+ }
+ if (r != 0)
+ continue;
+
+ if (timeout > 0) {
+ usec_t n;
+
+ n = now(CLOCK_MONOTONIC);
+ if (n >= timeout)
+ return -ETIMEDOUT;
+
+ left = timeout - n;
+ } else
+ left = (uint64_t) -1;
+
+ r = rtnl_poll(nl, true, left);
+ if (r < 0)
+ return r;
+
+ r = dispatch_wqueue(nl);
+ if (r < 0)
+ return r;
+ }
+}
+
+int sd_rtnl_flush(sd_rtnl *rtnl) {
+ int r;
+
+ assert_return(rtnl, -EINVAL);
+ assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+
+ if (rtnl->wqueue_size <= 0)
+ return 0;
+
+ for (;;) {
+ r = dispatch_wqueue(rtnl);
+ if (r < 0)
+ return r;
+
+ if (rtnl->wqueue_size <= 0)
+ return 0;
+
+ r = rtnl_poll(rtnl, false, (uint64_t) -1);
+ if (r < 0)
+ return r;
+ }
+}
+
+int sd_rtnl_get_events(sd_rtnl *rtnl) {
+ int flags = 0;
+
+ assert_return(rtnl, -EINVAL);
+ assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+
+ if (rtnl->rqueue_size <= 0)
+ flags |= POLLIN;
+ if (rtnl->wqueue_size > 0)
+ flags |= POLLOUT;
+
+ return flags;
+}
+
+int sd_rtnl_get_timeout(sd_rtnl *rtnl, uint64_t *timeout_usec) {
+ struct reply_callback *c;
+
+ assert_return(rtnl, -EINVAL);
+ assert_return(timeout_usec, -EINVAL);
+ assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+
+ if (rtnl->rqueue_size > 0) {
+ *timeout_usec = 0;
+ return 1;
+ }
+
+ c = prioq_peek(rtnl->reply_callbacks_prioq);
+ if (!c) {
+ *timeout_usec = (uint64_t) -1;
+ return 0;
+ }
+
+ *timeout_usec = c->timeout;
+
+ return 1;
+}
+
+static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ sd_rtnl *rtnl = userdata;
+ int r;
+
+ assert(rtnl);
+
+ r = sd_rtnl_process(rtnl, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_rtnl *rtnl = userdata;
+ int r;
+
+ assert(rtnl);
+
+ r = sd_rtnl_process(rtnl, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int prepare_callback(sd_event_source *s, void *userdata) {
+ sd_rtnl *rtnl = userdata;
+ int r, e;
+ usec_t until;
+
+ assert(s);
+ assert(rtnl);
+
+ e = sd_rtnl_get_events(rtnl);
+ if (e < 0)
+ return e;
+
+ r = sd_event_source_set_io_events(rtnl->io_event_source, e);
+ if (r < 0)
+ return r;
+
+ r = sd_rtnl_get_timeout(rtnl, &until);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ int j;
+
+ j = sd_event_source_set_time(rtnl->time_event_source, until);
+ if (j < 0)
+ return j;
+ }
+
+ r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int exit_callback(sd_event_source *event, void *userdata) {
+ sd_rtnl *rtnl = userdata;
+
+ assert(event);
+
+ sd_rtnl_flush(rtnl);
+
+ return 1;
+}
+
+int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
+ int r;
+
+ assert_return(rtnl, -EINVAL);
+ assert_return(!rtnl->event, -EBUSY);
+
+ assert(!rtnl->io_event_source);
+ assert(!rtnl->time_event_source);
+
+ if (event)
+ rtnl->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&rtnl->event);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_event_add_io(rtnl->event, rtnl->fd, 0, io_callback, rtnl, &rtnl->io_event_source);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_priority(rtnl->io_event_source, priority);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_add_monotonic(rtnl->event, 0, 0, time_callback, rtnl, &rtnl->time_event_source);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_priority(rtnl->time_event_source, priority);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_add_exit(rtnl->event, exit_callback, rtnl, &rtnl->exit_event_source);
+ if (r < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ sd_rtnl_detach_event(rtnl);
+ return r;
+}
+
+int sd_rtnl_detach_event(sd_rtnl *rtnl) {
+ assert_return(rtnl, -EINVAL);
+ assert_return(rtnl->event, -ENXIO);
+
+ if (rtnl->io_event_source)
+ rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
+
+ if (rtnl->time_event_source)
+ rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
+
+ if (rtnl->exit_event_source)
+ rtnl->exit_event_source = sd_event_source_unref(rtnl->exit_event_source);
+
+ if (rtnl->event)
+ rtnl->event = sd_event_unref(rtnl->event);
+
+ return 0;
+}
+
+int sd_rtnl_add_match(sd_rtnl *rtnl,
+ uint16_t type,
+ sd_rtnl_message_handler_t callback,
+ void *userdata) {
+ struct match_callback *c;
+
+ assert_return(rtnl, -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+ assert_return(message_type_is_link(type) || message_type_is_addr(type) || message_type_is_route(type), -ENOTSUP);
+
+ c = new0(struct match_callback, 1);
+ if (!c)
+ return -ENOMEM;
+
+ c->callback = callback;
+ c->type = type;
+ c->userdata = userdata;
+
+ LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
+
+ return 0;
+}
+
+int sd_rtnl_remove_match(sd_rtnl *rtnl,
+ uint16_t type,
+ sd_rtnl_message_handler_t callback,
+ void *userdata) {
+ struct match_callback *c;
+
+ assert_return(rtnl, -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+
+ LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
+ if (c->callback == callback && c->type == type && c->userdata == userdata) {
+ LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);
+ free(c);
+
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-rtnl/test-rtnl.c b/src/libsystemd/sd-rtnl/test-rtnl.c
new file mode 100644
index 0000000..58654e9
--- /dev/null
+++ b/src/libsystemd/sd-rtnl/test-rtnl.c
@@ -0,0 +1,330 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Tom Gundersen <teg at jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/rtnetlink.h>
+#include <netinet/ether.h>
+
+#include "util.h"
+#include "macro.h"
+#include "sd-rtnl.h"
+#include "socket-util.h"
+#include "rtnl-util.h"
+#include "event-util.h"
+
+static void test_link_configure(sd_rtnl *rtnl, int ifindex) {
+ _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message;
+ uint16_t type;
+ const char *mac = "98:fe:94:3f:c6:18", *name = "test";
+ unsigned int mtu = 1450;
+ void *data;
+
+ /* we'd really like to test NEWLINK, but let's not mess with the running kernel */
+ assert(sd_rtnl_message_link_new(RTM_GETLINK, ifindex, &message) >= 0);
+ assert(sd_rtnl_message_append_string(message, IFLA_IFNAME, name) >= 0);
+ assert(sd_rtnl_message_append_ether_addr(message, IFLA_ADDRESS, ether_aton(mac)) >= 0);
+ assert(sd_rtnl_message_append_u32(message, IFLA_MTU, mtu) >= 0);
+
+ assert(sd_rtnl_message_read(message, &type, &data) > 0);
+ assert(type == IFLA_IFNAME);
+ assert(streq(name, (char *) data));
+
+ assert(sd_rtnl_message_read(message, &type, &data) > 0);
+ assert(type == IFLA_ADDRESS);
+ assert(streq(mac, ether_ntoa(data)));
+
+ assert(sd_rtnl_message_read(message, &type, &data) > 0);
+ assert(type == IFLA_MTU);
+ assert(mtu == *(unsigned int *) data);
+
+ assert(sd_rtnl_call(rtnl, message, 0, NULL) == 1);
+}
+
+static void test_route(void) {
+ _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req;
+ struct in_addr addr;
+ uint32_t index = 2;
+ uint16_t type;
+ void *data;
+ int r;
+
+ r = sd_rtnl_message_route_new(RTM_NEWROUTE, AF_INET, &req);
+ if (r < 0) {
+ log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r));
+ return;
+ }
+
+ addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &addr);
+ if (r < 0) {
+ log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r));
+ return;
+ }
+
+ r = sd_rtnl_message_append_u32(req, RTA_OIF, index);
+ if (r < 0) {
+ log_error("Could not append RTA_OIF attribute: %s", strerror(-r));
+ return;
+ }
+
+ assert(sd_rtnl_message_read(req, &type, &data) > 0);
+ assert(type == RTA_GATEWAY);
+ assert(((struct in_addr *)data)->s_addr == addr.s_addr);
+
+ assert(sd_rtnl_message_read(req, &type, &data) > 0);
+ assert(type == RTA_OIF);
+ assert(*(uint32_t *) data == index);
+}
+
+static void test_multiple(void) {
+ sd_rtnl *rtnl1, *rtnl2;
+
+ assert(sd_rtnl_open(0, &rtnl1) >= 0);
+ assert(sd_rtnl_open(0, &rtnl2) >= 0);
+
+ rtnl1 = sd_rtnl_unref(rtnl1);
+ rtnl2 = sd_rtnl_unref(rtnl2);
+}
+
+static int link_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
+ void *data;
+ uint16_t type;
+ char *ifname = userdata;
+
+ assert(rtnl);
+ assert(m);
+
+ log_info("got link info about %s", ifname);
+ free(ifname);
+
+ while (sd_rtnl_message_read(m, &type, &data) > 0) {
+ switch (type) {
+// case IFLA_MTU:
+// assert(*(unsigned int *) data == 65536);
+// break;
+// case IFLA_QDISC:
+// assert(streq((char *) data, "noqueue"));
+// break;
+ case IFLA_IFNAME:
+ assert(streq((char *) data, "lo"));
+ break;
+ }
+ }
+
+ return 1;
+}
+
+static void test_event_loop(int ifindex) {
+ _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
+ _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
+ char *ifname;
+
+ ifname = strdup("lo2");
+ assert(ifname);
+
+ assert(sd_rtnl_open(0, &rtnl) >= 0);
+ assert(sd_rtnl_message_link_new(RTM_GETLINK, ifindex, &m) >= 0);
+
+ assert(sd_rtnl_call_async(rtnl, m, &link_handler, ifname, 0, NULL) >= 0);
+
+ assert(sd_event_default(&event) >= 0);
+
+ assert(sd_rtnl_attach_event(rtnl, event, 0) >= 0);
+
+ assert(sd_event_run(event, 0) >= 0);
+
+ assert(sd_rtnl_detach_event(rtnl) >= 0);
+}
+
+static int pipe_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
+ int *counter = userdata;
+
+ (*counter) --;
+
+ log_info("got reply, %d left in pipe", *counter);
+
+ return sd_rtnl_message_get_errno(m);
+}
+
+static void test_async(int ifindex) {
+ _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
+ _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL, *r = NULL;
+ uint32_t serial;
+ char *ifname;
+
+ ifname = strdup("lo");
+ assert(ifname);
+
+ assert(sd_rtnl_open(0, &rtnl) >= 0);
+
+ assert(sd_rtnl_message_link_new(RTM_GETLINK, ifindex, &m) >= 0);
+
+ assert(sd_rtnl_call_async(rtnl, m, &link_handler, ifname, 0, &serial) >= 0);
+
+ assert(sd_rtnl_wait(rtnl, 0) >= 0);
+ assert(sd_rtnl_process(rtnl, &r) >= 0);
+}
+
+static void test_pipe(int ifindex) {
+ _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
+ _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m1 = NULL, *m2 = NULL;
+ int counter = 0;
+
+ assert(sd_rtnl_open(0, &rtnl) >= 0);
+
+ assert(sd_rtnl_message_link_new(RTM_GETLINK, ifindex, &m1) >= 0);
+ assert(sd_rtnl_message_link_new(RTM_GETLINK, ifindex, &m2) >= 0);
+
+ counter ++;
+ assert(sd_rtnl_call_async(rtnl, m1, &pipe_handler, &counter, 0, NULL) >= 0);
+
+ counter ++;
+ assert(sd_rtnl_call_async(rtnl, m2, &pipe_handler, &counter, 0, NULL) >= 0);
+
+ while (counter > 0) {
+ assert(sd_rtnl_wait(rtnl, 0) >= 0);
+ assert(sd_rtnl_process(rtnl, NULL) >= 0);
+ }
+}
+
+static void test_container(void) {
+ _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
+ uint16_t type;
+ void *data;
+
+ assert(sd_rtnl_message_link_new(RTM_NEWLINK, 0, &m) >= 0);
+
+ assert(sd_rtnl_message_open_container(m, IFLA_LINKINFO) >= 0);
+ assert(sd_rtnl_message_open_container(m, IFLA_LINKINFO) == -EINVAL);
+ assert(sd_rtnl_message_append_string(m, IFLA_INFO_KIND, "kind") >= 0);
+ assert(sd_rtnl_message_close_container(m) >= 0);
+ assert(sd_rtnl_message_close_container(m) == -EINVAL);
+
+ assert(sd_rtnl_message_read(m, &type, &data) == -EINVAL);
+
+/* TODO: add support for entering containers
+ assert(sd_rtnl_message_read(m, &type, &data) > 0);
+ assert(type == IFLA_INFO_KIND);
+ assert(streq("kind", (char *) data));
+
+ assert(sd_rtnl_message_read(m, &type, &data) == 0);
+*/
+}
+
+static void test_match(void) {
+ _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
+
+ assert(sd_rtnl_open(0, &rtnl) >= 0);
+
+ assert(sd_rtnl_add_match(rtnl, RTM_NEWLINK, &link_handler, NULL) >= 0);
+ assert(sd_rtnl_add_match(rtnl, RTM_NEWLINK, &link_handler, NULL) >= 0);
+
+ assert(sd_rtnl_remove_match(rtnl, RTM_NEWLINK, &link_handler, NULL) == 1);
+ assert(sd_rtnl_remove_match(rtnl, RTM_NEWLINK, &link_handler, NULL) == 1);
+ assert(sd_rtnl_remove_match(rtnl, RTM_NEWLINK, &link_handler, NULL) == 0);
+}
+
+int main(void) {
+ sd_rtnl *rtnl;
+ sd_rtnl_message *m;
+ sd_rtnl_message *r;
+ void *data;
+ int if_loopback;
+ uint16_t type;
+ unsigned int mtu = 0;
+ unsigned int *mtu_reply;
+
+ test_match();
+
+ test_multiple();
+
+ test_route();
+
+ test_container();
+
+ assert(sd_rtnl_open(0, &rtnl) >= 0);
+ assert(rtnl);
+
+ if_loopback = (int) if_nametoindex("lo");
+ assert(if_loopback > 0);
+
+ test_async(if_loopback);
+
+ test_pipe(if_loopback);
+
+ test_event_loop(if_loopback);
+
+ test_link_configure(rtnl, if_loopback);
+
+ assert(sd_rtnl_message_link_new(RTM_GETLINK, if_loopback, &m) >= 0);
+ assert(m);
+
+ assert(sd_rtnl_message_get_type(m, &type) >= 0);
+ assert(type == RTM_GETLINK);
+
+ assert(sd_rtnl_message_read(m, &type, &data) == 0);
+
+ assert(sd_rtnl_call(rtnl, m, 0, &r) == 1);
+ assert(sd_rtnl_message_get_type(r, &type) >= 0);
+ assert(type == RTM_NEWLINK);
+
+ assert(sd_rtnl_message_read(m, &type, &data) == 0);
+ assert((r = sd_rtnl_message_unref(r)) == NULL);
+
+ assert(sd_rtnl_call(rtnl, m, -1, &r) == -EPERM);
+ assert((m = sd_rtnl_message_unref(m)) == NULL);
+ assert((r = sd_rtnl_message_unref(r)) == NULL);
+
+ assert(sd_rtnl_message_link_new(RTM_GETLINK, if_loopback, &m) >= 0);
+ assert(m);
+
+ assert(sd_rtnl_message_append_u32(m, IFLA_MTU, mtu) >= 0);
+ assert(sd_rtnl_message_read(m, &type, (void **) &mtu_reply) == 1);
+
+ assert(type == IFLA_MTU);
+ assert(*mtu_reply == 0);
+
+ assert(sd_rtnl_message_read(m, &type, &data) == 0);
+
+ assert(sd_rtnl_call(rtnl, m, -1, &r) == 1);
+ while (sd_rtnl_message_read(r, &type, &data) > 0) {
+ switch (type) {
+// case IFLA_MTU:
+// assert(*(unsigned int *) data == 65536);
+// break;
+// case IFLA_QDISC:
+// assert(streq((char *) data, "noqueue"));
+// break;
+ case IFLA_IFNAME:
+ assert(streq((char *) data, "lo"));
+ break;
+ }
+ }
+
+ assert(sd_rtnl_flush(rtnl) >= 0);
+
+ assert((m = sd_rtnl_message_unref(m)) == NULL);
+ assert((r = sd_rtnl_message_unref(r)) == NULL);
+ assert((rtnl = sd_rtnl_unref(rtnl)) == NULL);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/libsystemd/sd-utf8.c b/src/libsystemd/sd-utf8.c
deleted file mode 100644
index 6f2aa60..0000000
--- a/src/libsystemd/sd-utf8.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-#include "utf8.h"
-#include "sd-utf8.h"
-
-_public_ const char *sd_utf8_is_valid(const char *s) {
- assert_return(s, NULL);
-
- return utf8_is_valid(s);
-}
-
-_public_ const char *sd_ascii_is_valid(const char *s) {
- assert_return(s, NULL);
-
- return ascii_is_valid(s);
-}
diff --git a/src/libsystemd/sd-utf8/Makefile b/src/libsystemd/sd-utf8/Makefile
new file mode 120000
index 0000000..94aaae2
--- /dev/null
+++ b/src/libsystemd/sd-utf8/Makefile
@@ -0,0 +1 @@
+../../Makefile
\ No newline at end of file
diff --git a/src/libsystemd/sd-utf8/sd-utf8.c b/src/libsystemd/sd-utf8/sd-utf8.c
new file mode 100644
index 0000000..6f2aa60
--- /dev/null
+++ b/src/libsystemd/sd-utf8/sd-utf8.c
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "utf8.h"
+#include "sd-utf8.h"
+
+_public_ const char *sd_utf8_is_valid(const char *s) {
+ assert_return(s, NULL);
+
+ return utf8_is_valid(s);
+}
+
+_public_ const char *sd_ascii_is_valid(const char *s) {
+ assert_return(s, NULL);
+
+ return ascii_is_valid(s);
+}
diff --git a/src/libsystemd/test-bus-chat.c b/src/libsystemd/test-bus-chat.c
deleted file mode 100644
index 021379f..0000000
--- a/src/libsystemd/test-bus-chat.c
+++ /dev/null
@@ -1,581 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "log.h"
-#include "util.h"
-#include "macro.h"
-
-#include "sd-bus.h"
-#include "bus-message.h"
-#include "bus-error.h"
-#include "bus-match.h"
-#include "bus-internal.h"
-#include "bus-util.h"
-
-static int match_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
- log_info("Match triggered! interface=%s member=%s", strna(sd_bus_message_get_interface(m)), strna(sd_bus_message_get_member(m)));
- return 0;
-}
-
-static int object_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
- int r;
-
- assert(bus);
-
- if (sd_bus_message_is_method_error(m, NULL))
- return 0;
-
- if (sd_bus_message_is_method_call(m, "org.object.test", "Foobar")) {
- log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m));
-
- r = sd_bus_reply_method_return(m, NULL);
- if (r < 0) {
- log_error("Failed to send reply: %s", strerror(-r));
- return r;
- }
-
- return 1;
- }
-
- return 0;
-}
-
-static int server_init(sd_bus **_bus) {
- sd_bus *bus = NULL;
- sd_id128_t id;
- int r;
- const char *unique;
-
- assert(_bus);
-
- r = sd_bus_open_user(&bus);
- if (r < 0) {
- log_error("Failed to connect to user bus: %s", strerror(-r));
- goto fail;
- }
-
- r = sd_bus_get_server_id(bus, &id);
- if (r < 0) {
- log_error("Failed to get server ID: %s", strerror(-r));
- goto fail;
- }
-
- r = sd_bus_get_unique_name(bus, &unique);
- if (r < 0) {
- log_error("Failed to get unique name: %s", strerror(-r));
- goto fail;
- }
-
- log_info("Peer ID is " SD_ID128_FORMAT_STR ".", SD_ID128_FORMAT_VAL(id));
- log_info("Unique ID: %s", unique);
- log_info("Can send file handles: %i", sd_bus_can_send(bus, 'h'));
-
- r = sd_bus_request_name(bus, "org.freedesktop.systemd.test", 0);
- if (r < 0) {
- log_error("Failed to acquire name: %s", strerror(-r));
- goto fail;
- }
-
- r = sd_bus_add_fallback(bus, "/foo/bar", object_callback, NULL);
- if (r < 0) {
- log_error("Failed to add object: %s", strerror(-r));
- goto fail;
- }
-
- r = sd_bus_add_match(bus, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL);
- if (r < 0) {
- log_error("Failed to add match: %s", strerror(-r));
- goto fail;
- }
-
- r = sd_bus_add_match(bus, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL);
- if (r < 0) {
- log_error("Failed to add match: %s", strerror(-r));
- goto fail;
- }
-
- bus_match_dump(&bus->match_callbacks, 0);
-
- *_bus = bus;
- return 0;
-
-fail:
- if (bus)
- sd_bus_unref(bus);
-
- return r;
-}
-
-static int server(sd_bus *bus) {
- int r;
- bool client1_gone = false, client2_gone = false;
-
- while (!client1_gone || !client2_gone) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- pid_t pid = 0;
- const char *label = NULL;
-
- r = sd_bus_process(bus, &m);
- if (r < 0) {
- log_error("Failed to process requests: %s", strerror(-r));
- goto fail;
- }
-
- if (r == 0) {
- r = sd_bus_wait(bus, (uint64_t) -1);
- if (r < 0) {
- log_error("Failed to wait: %s", strerror(-r));
- goto fail;
- }
-
- continue;
- }
-
- if (!m)
- continue;
-
- sd_bus_creds_get_pid(sd_bus_message_get_creds(m), &pid);
- sd_bus_creds_get_selinux_context(sd_bus_message_get_creds(m), &label);
- log_info("Got message! member=%s pid=%lu label=%s",
- strna(sd_bus_message_get_member(m)),
- (unsigned long) pid,
- strna(label));
- /* bus_message_dump(m); */
- /* sd_bus_message_rewind(m, true); */
-
- if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "LowerCase")) {
- const char *hello;
- _cleanup_free_ char *lowercase = NULL;
-
- r = sd_bus_message_read(m, "s", &hello);
- if (r < 0) {
- log_error("Failed to get parameter: %s", strerror(-r));
- goto fail;
- }
-
- lowercase = strdup(hello);
- if (!lowercase) {
- r = log_oom();
- goto fail;
- }
-
- ascii_strlower(lowercase);
-
- r = sd_bus_reply_method_return(m, "s", lowercase);
- if (r < 0) {
- log_error("Failed to send reply: %s", strerror(-r));
- goto fail;
- }
- } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) {
-
- r = sd_bus_reply_method_return(m, NULL);
- if (r < 0) {
- log_error("Failed to send reply: %s", strerror(-r));
- goto fail;
- }
-
- client1_gone = true;
- } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient2")) {
-
- r = sd_bus_reply_method_return(m, NULL);
- if (r < 0) {
- log_error("Failed to send reply: %s", strerror(-r));
- goto fail;
- }
-
- client2_gone = true;
- } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Slow")) {
-
- sleep(1);
-
- r = sd_bus_reply_method_return(m, NULL);
- if (r < 0) {
- log_error("Failed to send reply: %s", strerror(-r));
- goto fail;
- }
-
- } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "FileDescriptor")) {
- int fd;
- static const char x = 'X';
-
- r = sd_bus_message_read(m, "h", &fd);
- if (r < 0) {
- log_error("Failed to get parameter: %s", strerror(-r));
- goto fail;
- }
-
- log_info("Received fd=%d", fd);
-
- if (write(fd, &x, 1) < 0) {
- log_error("Failed to write to fd: %m");
- close_nointr_nofail(fd);
- goto fail;
- }
-
- r = sd_bus_reply_method_return(m, NULL);
- if (r < 0) {
- log_error("Failed to send reply: %s", strerror(-r));
- goto fail;
- }
-
- } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
-
- r = sd_bus_reply_method_error(
- m,
- &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method."));
- if (r < 0) {
- log_error("Failed to send reply: %s", strerror(-r));
- goto fail;
- }
- }
- }
-
- r = 0;
-
-fail:
- if (bus) {
- sd_bus_flush(bus);
- sd_bus_unref(bus);
- }
-
- return r;
-}
-
-static void* client1(void*p) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- sd_bus *bus = NULL;
- sd_bus_error error = SD_BUS_ERROR_NULL;
- const char *hello;
- int r;
- int pp[2] = { -1, -1 };
- char x;
-
- r = sd_bus_open_user(&bus);
- if (r < 0) {
- log_error("Failed to connect to user bus: %s", strerror(-r));
- goto finish;
- }
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.systemd.test",
- "/",
- "org.freedesktop.systemd.test",
- "LowerCase",
- &error,
- &reply,
- "s",
- "HELLO");
- if (r < 0) {
- log_error("Failed to issue method call: %s", strerror(-r));
- goto finish;
- }
-
- r = sd_bus_message_read(reply, "s", &hello);
- if (r < 0) {
- log_error("Failed to get string: %s", strerror(-r));
- goto finish;
- }
-
- assert(streq(hello, "hello"));
-
- if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) {
- log_error("Failed to allocate pipe: %m");
- r = -errno;
- goto finish;
- }
-
- log_info("Sending fd=%d", pp[1]);
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.systemd.test",
- "/",
- "org.freedesktop.systemd.test",
- "FileDescriptor",
- &error,
- NULL,
- "h",
- pp[1]);
- if (r < 0) {
- log_error("Failed to issue method call: %s", strerror(-r));
- goto finish;
- }
-
- errno = 0;
- if (read(pp[0], &x, 1) <= 0) {
- log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read");
- goto finish;
- }
-
- r = 0;
-
-finish:
- if (bus) {
- _cleanup_bus_message_unref_ sd_bus_message *q;
-
- r = sd_bus_message_new_method_call(
- bus,
- "org.freedesktop.systemd.test",
- "/",
- "org.freedesktop.systemd.test",
- "ExitClient1",
- &q);
- if (r < 0)
- log_error("Failed to allocate method call: %s", strerror(-r));
- else
- sd_bus_send(bus, q, NULL);
-
- sd_bus_flush(bus);
- sd_bus_unref(bus);
- }
-
- sd_bus_error_free(&error);
-
- close_pipe(pp);
-
- return INT_TO_PTR(r);
-}
-
-static int quit_callback(sd_bus *b, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
- bool *x = userdata;
-
- log_error("Quit callback: %s", strerror(sd_bus_message_get_errno(m)));
-
- *x = 1;
- return 1;
-}
-
-static void* client2(void*p) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
- sd_bus *bus = NULL;
- sd_bus_error error = SD_BUS_ERROR_NULL;
- bool quit = false;
- const char *mid;
- int r;
-
- r = sd_bus_open_user(&bus);
- if (r < 0) {
- log_error("Failed to connect to user bus: %s", strerror(-r));
- goto finish;
- }
-
- r = sd_bus_message_new_method_call(
- bus,
- "org.freedesktop.systemd.test",
- "/foo/bar/waldo/piep",
- "org.object.test",
- "Foobar",
- &m);
- if (r < 0) {
- log_error("Failed to allocate method call: %s", strerror(-r));
- goto finish;
- }
-
- r = sd_bus_send(bus, m, NULL);
- if (r < 0) {
- log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
- goto finish;
- }
-
- sd_bus_message_unref(m);
- m = NULL;
-
- r = sd_bus_message_new_signal(
- bus,
- "/foobar",
- "foo.bar",
- "Notify",
- &m);
- if (r < 0) {
- log_error("Failed to allocate signal: %s", strerror(-r));
- goto finish;
- }
-
- r = sd_bus_send(bus, m, NULL);
- if (r < 0) {
- log_error("Failed to issue signal: %s", bus_error_message(&error, -r));
- goto finish;
- }
-
- sd_bus_message_unref(m);
- m = NULL;
-
- r = sd_bus_message_new_method_call(
- bus,
- "org.freedesktop.systemd.test",
- "/",
- "org.freedesktop.DBus.Peer",
- "GetMachineId",
- &m);
- if (r < 0) {
- log_error("Failed to allocate method call: %s", strerror(-r));
- goto finish;
- }
-
- r = sd_bus_call(bus, m, 0, &error, &reply);
- if (r < 0) {
- log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
- goto finish;
- }
-
- r = sd_bus_message_read(reply, "s", &mid);
- if (r < 0) {
- log_error("Failed to parse machine ID: %s", strerror(-r));
- goto finish;
- }
-
- log_info("Machine ID is %s.", mid);
-
- sd_bus_message_unref(m);
- m = NULL;
-
- r = sd_bus_message_new_method_call(
- bus,
- "org.freedesktop.systemd.test",
- "/",
- "org.freedesktop.systemd.test",
- "Slow",
- &m);
- if (r < 0) {
- log_error("Failed to allocate method call: %s", strerror(-r));
- goto finish;
- }
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_call(bus, m, 200 * USEC_PER_MSEC, &error, &reply);
- if (r < 0)
- log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
- else
- log_info("Slow call succeed.");
-
- sd_bus_message_unref(m);
- m = NULL;
-
- r = sd_bus_message_new_method_call(
- bus,
- "org.freedesktop.systemd.test",
- "/",
- "org.freedesktop.systemd.test",
- "Slow",
- &m);
- if (r < 0) {
- log_error("Failed to allocate method call: %s", strerror(-r));
- goto finish;
- }
-
- r = sd_bus_call_async(bus, m, quit_callback, &quit, 200 * USEC_PER_MSEC, NULL);
- if (r < 0) {
- log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
- goto finish;
- }
-
- while (!quit) {
- r = sd_bus_process(bus, NULL);
- if (r < 0) {
- log_error("Failed to process requests: %s", strerror(-r));
- goto finish;
- }
- if (r == 0) {
- r = sd_bus_wait(bus, (uint64_t) -1);
- if (r < 0) {
- log_error("Failed to wait: %s", strerror(-r));
- goto finish;
- }
- }
- }
-
- r = 0;
-
-finish:
- if (bus) {
- _cleanup_bus_message_unref_ sd_bus_message *q;
-
- r = sd_bus_message_new_method_call(
- bus,
- "org.freedesktop.systemd.test",
- "/",
- "org.freedesktop.systemd.test",
- "ExitClient2",
- &q);
- if (r < 0) {
- log_error("Failed to allocate method call: %s", strerror(-r));
- goto finish;
- }
-
- sd_bus_send(bus, q, NULL);
- sd_bus_flush(bus);
- sd_bus_unref(bus);
- }
-
- sd_bus_error_free(&error);
- return INT_TO_PTR(r);
-}
-
-int main(int argc, char *argv[]) {
- pthread_t c1, c2;
- sd_bus *bus;
- void *p;
- int q, r;
-
- r = server_init(&bus);
- if (r < 0) {
- log_info("Failed to connect to bus, skipping tests.");
- return EXIT_TEST_SKIP;
- }
-
- log_info("Initialized...");
-
- r = pthread_create(&c1, NULL, client1, bus);
- if (r != 0)
- return EXIT_FAILURE;
-
- r = pthread_create(&c2, NULL, client2, bus);
- if (r != 0)
- return EXIT_FAILURE;
-
- r = server(bus);
-
- q = pthread_join(c1, &p);
- if (q != 0)
- return EXIT_FAILURE;
- if (PTR_TO_INT(p) < 0)
- return EXIT_FAILURE;
-
- q = pthread_join(c2, &p);
- if (q != 0)
- return EXIT_FAILURE;
- if (PTR_TO_INT(p) < 0)
- return EXIT_FAILURE;
-
- if (r < 0)
- return EXIT_FAILURE;
-
- return EXIT_SUCCESS;
-}
diff --git a/src/libsystemd/test-bus-cleanup.c b/src/libsystemd/test-bus-cleanup.c
deleted file mode 100644
index d8ddb84..0000000
--- a/src/libsystemd/test-bus-cleanup.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Zbigniew JÄdrzejewski-Szmek
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdio.h>
-
-#include "sd-bus.h"
-#include "bus-util.h"
-#include "bus-internal.h"
-#include "bus-message.h"
-#include "refcnt.h"
-
-static void test_bus_new(void) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
-
- assert_se(sd_bus_new(&bus) == 0);
- printf("after new: refcount %u\n", REFCNT_GET(bus->n_ref));
-}
-
-static void test_bus_open(void) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
-
- assert_se(sd_bus_open_system(&bus) >= 0);
- printf("after open: refcount %u\n", REFCNT_GET(bus->n_ref));
-}
-
-static void test_bus_new_method_call(void) {
- sd_bus *bus = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-
- assert_se(sd_bus_open_system(&bus) >= 0);
-
- assert_se(sd_bus_message_new_method_call(bus, "a.service.name", "/an/object/path", "an.interface.name", "AMethodName", &m) >= 0);
-
- printf("after message_new_method_call: refcount %u\n", REFCNT_GET(bus->n_ref));
-
- sd_bus_unref(bus);
- printf("after bus_unref: refcount %u\n", m->n_ref);
-}
-
-static void test_bus_new_signal(void) {
- sd_bus *bus = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-
- assert_se(sd_bus_open_system(&bus) >= 0);
-
- assert_se(sd_bus_message_new_signal(bus, "/an/object/path", "an.interface.name", "Name", &m) >= 0);
-
- printf("after message_new_signal: refcount %u\n", REFCNT_GET(bus->n_ref));
-
- sd_bus_unref(bus);
- printf("after bus_unref: refcount %u\n", m->n_ref);
-}
-
-int main(int argc, char **argv) {
- log_parse_environment();
- log_open();
-
- test_bus_new();
- test_bus_open();
- test_bus_new_method_call();
- test_bus_new_signal();
-}
diff --git a/src/libsystemd/test-bus-creds.c b/src/libsystemd/test-bus-creds.c
deleted file mode 100644
index 966b84c..0000000
--- a/src/libsystemd/test-bus-creds.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "sd-bus.h"
-#include "bus-dump.h"
-#include "bus-util.h"
-#include "util.h"
-
-int main(int argc, char *argv[]) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
- int r;
-
- r = sd_bus_creds_new_from_pid(0, _SD_BUS_CREDS_ALL, &creds);
- assert_se(r >= 0);
-
- bus_creds_dump(creds, NULL);
-
- creds = sd_bus_creds_unref(creds);
-
- r = sd_bus_creds_new_from_pid(1, _SD_BUS_CREDS_ALL, &creds);
- if (r != -EACCES) {
- assert_se(r >= 0);
- putchar('\n');
- bus_creds_dump(creds, NULL);
- }
-
- return 0;
-}
diff --git a/src/libsystemd/test-bus-error.c b/src/libsystemd/test-bus-error.c
deleted file mode 100644
index b78be54..0000000
--- a/src/libsystemd/test-bus-error.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "sd-bus.h"
-#include "bus-error.h"
-#include "bus-util.h"
-
-int main(int argc, char *argv[]) {
-
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL, second = SD_BUS_ERROR_NULL;
- const sd_bus_error const_error = SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "const error");
- const sd_bus_error temporarily_const_error = {
- .name = SD_BUS_ERROR_ACCESS_DENIED,
- .message = "oh! no",
- ._need_free = -1
- };
-
- assert_se(!sd_bus_error_is_set(&error));
- assert_se(sd_bus_error_set(&error, SD_BUS_ERROR_NOT_SUPPORTED, "xxx") == -ENOTSUP);
- assert_se(streq(error.name, SD_BUS_ERROR_NOT_SUPPORTED));
- assert_se(streq(error.message, "xxx"));
- assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_NOT_SUPPORTED));
- assert_se(sd_bus_error_get_errno(&error) == ENOTSUP);
- assert_se(sd_bus_error_is_set(&error));
- sd_bus_error_free(&error);
-
- assert_se(!sd_bus_error_is_set(&error));
- assert_se(sd_bus_error_setf(&error, SD_BUS_ERROR_FILE_NOT_FOUND, "yyy %i", -1) == -ENOENT);
- assert_se(streq(error.name, SD_BUS_ERROR_FILE_NOT_FOUND));
- assert_se(streq(error.message, "yyy -1"));
- assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND));
- assert_se(sd_bus_error_get_errno(&error) == ENOENT);
- assert_se(sd_bus_error_is_set(&error));
-
- assert_se(!sd_bus_error_is_set(&second));
- assert_se(second._need_free == 0);
- assert_se(error._need_free > 0);
- assert_se(sd_bus_error_copy(&second, &error) == -ENOENT);
- assert_se(second._need_free > 0);
- assert_se(streq(error.name, second.name));
- assert_se(streq(error.message, second.message));
- assert_se(sd_bus_error_get_errno(&second) == ENOENT);
- assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_FILE_NOT_FOUND));
- assert_se(sd_bus_error_is_set(&second));
-
- sd_bus_error_free(&error);
- sd_bus_error_free(&second);
-
- assert_se(!sd_bus_error_is_set(&second));
- assert_se(const_error._need_free == 0);
- assert_se(sd_bus_error_copy(&second, &const_error) == -EEXIST);
- assert_se(second._need_free == 0);
- assert_se(streq(const_error.name, second.name));
- assert_se(streq(const_error.message, second.message));
- assert_se(sd_bus_error_get_errno(&second) == EEXIST);
- assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_FILE_EXISTS));
- assert_se(sd_bus_error_is_set(&second));
- sd_bus_error_free(&second);
-
- assert_se(!sd_bus_error_is_set(&second));
- assert_se(temporarily_const_error._need_free < 0);
- assert_se(sd_bus_error_copy(&second, &temporarily_const_error) == -EACCES);
- assert_se(second._need_free > 0);
- assert_se(streq(temporarily_const_error.name, second.name));
- assert_se(streq(temporarily_const_error.message, second.message));
- assert_se(sd_bus_error_get_errno(&second) == EACCES);
- assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_ACCESS_DENIED));
- assert_se(sd_bus_error_is_set(&second));
-
- assert_se(!sd_bus_error_is_set(&error));
- assert_se(sd_bus_error_set_const(&error, "System.Error.EUCLEAN", "Hallo") == -EUCLEAN);
- assert_se(streq(error.name, "System.Error.EUCLEAN"));
- assert_se(streq(error.message, "Hallo"));
- assert_se(sd_bus_error_has_name(&error, "System.Error.EUCLEAN"));
- assert_se(sd_bus_error_get_errno(&error) == EUCLEAN);
- assert_se(sd_bus_error_is_set(&error));
- sd_bus_error_free(&error);
-
- assert_se(!sd_bus_error_is_set(&error));
- assert_se(sd_bus_error_set_errno(&error, EBUSY) == -EBUSY);
- assert_se(streq(error.name, "System.Error.EBUSY"));
- assert_se(streq(error.message, strerror(EBUSY)));
- assert_se(sd_bus_error_has_name(&error, "System.Error.EBUSY"));
- assert_se(sd_bus_error_get_errno(&error) == EBUSY);
- assert_se(sd_bus_error_is_set(&error));
- sd_bus_error_free(&error);
-
- assert_se(!sd_bus_error_is_set(&error));
- assert_se(sd_bus_error_set_errnof(&error, EIO, "Waldi %c", 'X') == -EIO);
- assert_se(streq(error.name, SD_BUS_ERROR_IO_ERROR));
- assert_se(streq(error.message, "Waldi X"));
- assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR));
- assert_se(sd_bus_error_get_errno(&error) == EIO);
- assert_se(sd_bus_error_is_set(&error));
-
- return 0;
-}
diff --git a/src/libsystemd/test-bus-gvariant.c b/src/libsystemd/test-bus-gvariant.c
deleted file mode 100644
index cb07c96..0000000
--- a/src/libsystemd/test-bus-gvariant.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#ifdef HAVE_GLIB
-#include <glib.h>
-#endif
-
-#include "util.h"
-#include "sd-bus.h"
-#include "bus-gvariant.h"
-#include "bus-util.h"
-#include "bus-internal.h"
-#include "bus-message.h"
-#include "bus-dump.h"
-
-static void test_bus_gvariant_is_fixed_size(void) {
- assert(bus_gvariant_is_fixed_size("") > 0);
- assert(bus_gvariant_is_fixed_size("()") > 0);
- assert(bus_gvariant_is_fixed_size("y") > 0);
- assert(bus_gvariant_is_fixed_size("u") > 0);
- assert(bus_gvariant_is_fixed_size("b") > 0);
- assert(bus_gvariant_is_fixed_size("n") > 0);
- assert(bus_gvariant_is_fixed_size("q") > 0);
- assert(bus_gvariant_is_fixed_size("i") > 0);
- assert(bus_gvariant_is_fixed_size("t") > 0);
- assert(bus_gvariant_is_fixed_size("d") > 0);
- assert(bus_gvariant_is_fixed_size("s") == 0);
- assert(bus_gvariant_is_fixed_size("o") == 0);
- assert(bus_gvariant_is_fixed_size("g") == 0);
- assert(bus_gvariant_is_fixed_size("h") > 0);
- assert(bus_gvariant_is_fixed_size("ay") == 0);
- assert(bus_gvariant_is_fixed_size("v") == 0);
- assert(bus_gvariant_is_fixed_size("(u)") > 0);
- assert(bus_gvariant_is_fixed_size("(uuuuy)") > 0);
- assert(bus_gvariant_is_fixed_size("(uusuuy)") == 0);
- assert(bus_gvariant_is_fixed_size("a{ss}") == 0);
- assert(bus_gvariant_is_fixed_size("((u)yyy(b(iiii)))") > 0);
- assert(bus_gvariant_is_fixed_size("((u)yyy(b(iiivi)))") == 0);
-}
-
-static void test_bus_gvariant_get_size(void) {
- assert(bus_gvariant_get_size("") == 0);
- assert(bus_gvariant_get_size("()") == 0);
- assert(bus_gvariant_get_size("y") == 1);
- assert(bus_gvariant_get_size("u") == 4);
- assert(bus_gvariant_get_size("b") == 1);
- assert(bus_gvariant_get_size("n") == 2);
- assert(bus_gvariant_get_size("q") == 2);
- assert(bus_gvariant_get_size("i") == 4);
- assert(bus_gvariant_get_size("t") == 8);
- assert(bus_gvariant_get_size("d") == 8);
- assert(bus_gvariant_get_size("s") < 0);
- assert(bus_gvariant_get_size("o") < 0);
- assert(bus_gvariant_get_size("g") < 0);
- assert(bus_gvariant_get_size("h") == 4);
- assert(bus_gvariant_get_size("ay") < 0);
- assert(bus_gvariant_get_size("v") < 0);
- assert(bus_gvariant_get_size("(u)") == 4);
- assert(bus_gvariant_get_size("(uuuuy)") == 20);
- assert(bus_gvariant_get_size("(uusuuy)") < 0);
- assert(bus_gvariant_get_size("a{ss}") < 0);
- assert(bus_gvariant_get_size("((u)yyy(b(iiii)))") == 28);
- assert(bus_gvariant_get_size("((u)yyy(b(iiivi)))") < 0);
- assert(bus_gvariant_get_size("((b)(t))") == 16);
- assert(bus_gvariant_get_size("((b)(b)(t))") == 16);
- assert(bus_gvariant_get_size("(bt)") == 16);
- assert(bus_gvariant_get_size("((t)(b))") == 16);
- assert(bus_gvariant_get_size("(tb)") == 16);
- assert(bus_gvariant_get_size("((b)(b))") == 2);
- assert(bus_gvariant_get_size("((t)(t))") == 16);
-}
-
-static void test_bus_gvariant_get_alignment(void) {
- assert(bus_gvariant_get_alignment("") == 1);
- assert(bus_gvariant_get_alignment("()") == 1);
- assert(bus_gvariant_get_alignment("y") == 1);
- assert(bus_gvariant_get_alignment("b") == 1);
- assert(bus_gvariant_get_alignment("u") == 4);
- assert(bus_gvariant_get_alignment("s") == 1);
- assert(bus_gvariant_get_alignment("o") == 1);
- assert(bus_gvariant_get_alignment("g") == 1);
- assert(bus_gvariant_get_alignment("v") == 8);
- assert(bus_gvariant_get_alignment("h") == 4);
- assert(bus_gvariant_get_alignment("i") == 4);
- assert(bus_gvariant_get_alignment("t") == 8);
- assert(bus_gvariant_get_alignment("x") == 8);
- assert(bus_gvariant_get_alignment("q") == 2);
- assert(bus_gvariant_get_alignment("n") == 2);
- assert(bus_gvariant_get_alignment("d") == 8);
- assert(bus_gvariant_get_alignment("ay") == 1);
- assert(bus_gvariant_get_alignment("as") == 1);
- assert(bus_gvariant_get_alignment("au") == 4);
- assert(bus_gvariant_get_alignment("an") == 2);
- assert(bus_gvariant_get_alignment("ans") == 2);
- assert(bus_gvariant_get_alignment("ant") == 8);
- assert(bus_gvariant_get_alignment("(ss)") == 1);
- assert(bus_gvariant_get_alignment("(ssu)") == 4);
- assert(bus_gvariant_get_alignment("a(ssu)") == 4);
- assert(bus_gvariant_get_alignment("(u)") == 4);
- assert(bus_gvariant_get_alignment("(uuuuy)") == 4);
- assert(bus_gvariant_get_alignment("(uusuuy)") == 4);
- assert(bus_gvariant_get_alignment("a{ss}") == 1);
- assert(bus_gvariant_get_alignment("((u)yyy(b(iiii)))") == 4);
- assert(bus_gvariant_get_alignment("((u)yyy(b(iiivi)))") == 8);
- assert(bus_gvariant_get_alignment("((b)(t))") == 8);
- assert(bus_gvariant_get_alignment("((b)(b)(t))") == 8);
- assert(bus_gvariant_get_alignment("(bt)") == 8);
- assert(bus_gvariant_get_alignment("((t)(b))") == 8);
- assert(bus_gvariant_get_alignment("(tb)") == 8);
- assert(bus_gvariant_get_alignment("((b)(b))") == 1);
- assert(bus_gvariant_get_alignment("((t)(t))") == 8);
-}
-
-static void test_marshal(void) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *n = NULL;
- _cleanup_bus_unref_ sd_bus *bus = NULL;
- _cleanup_free_ void *blob;
- size_t sz;
-
- assert_se(sd_bus_open_system(&bus) >= 0);
- bus->message_version = 2; /* dirty hack to enable gvariant*/
-
- assert_se(sd_bus_message_new_method_call(bus, "a.service.name", "/an/object/path/which/is/really/really/long/so/that/we/hit/the/eight/bit/boundary/by/quite/some/margin/to/test/this/stuff/that/it/really/works", "an.interface.name", "AMethodName", &m) >= 0);
-
- assert_se(sd_bus_message_append(m,
- "a(usv)", 3,
- 4711, "first-string-parameter", "(st)", "X", (uint64_t) 1111,
- 4712, "second-string-parameter", "(a(si))", 2, "Y", 5, "Z", 6,
- 4713, "third-string-parameter", "(uu)", 1, 2) >= 0);
-
- assert_se(bus_message_seal(m, 4711, 0) >= 0);
-
-#ifdef HAVE_GLIB
- {
- GVariant *v;
- char *t;
-
-#if !defined(GLIB_VERSION_2_36)
- g_type_init();
-#endif
-
- v = g_variant_new_from_data(G_VARIANT_TYPE("(yyyyuuua(yv))"), m->header, sizeof(struct bus_header) + BUS_MESSAGE_FIELDS_SIZE(m), false, NULL, NULL);
- t = g_variant_print(v, TRUE);
- printf("%s\n", t);
- g_free(t);
- g_variant_unref(v);
-
- v = g_variant_new_from_data(G_VARIANT_TYPE("(a(usv))"), m->body.data, BUS_MESSAGE_BODY_SIZE(m), false, NULL, NULL);
- t = g_variant_print(v, TRUE);
- printf("%s\n", t);
- g_free(t);
- g_variant_unref(v);
- }
-#endif
-
- assert_se(bus_message_dump(m, NULL, true) >= 0);
-
- assert_se(bus_message_get_blob(m, &blob, &sz) >= 0);
-
- assert_se(bus_message_from_malloc(NULL, blob, sz, NULL, 0, NULL, NULL, &n) >= 0);
- blob = NULL;
-
- assert_se(bus_message_dump(n, NULL, true) >= 0);
-
- m = sd_bus_message_unref(m);
-
- assert_se(sd_bus_message_new_method_call(bus, "a.x", "/a/x", "a.x", "Ax", &m) >= 0);
-
- assert_se(sd_bus_message_append(m, "as", 0) >= 0);
-
- assert_se(bus_message_seal(m, 4712, 0) >= 0);
- assert_se(bus_message_dump(m, NULL, true) >= 0);
-}
-
-int main(int argc, char *argv[]) {
-
- test_bus_gvariant_is_fixed_size();
- test_bus_gvariant_get_size();
- test_bus_gvariant_get_alignment();
- test_marshal();
-
- return 0;
-}
diff --git a/src/libsystemd/test-bus-introspect.c b/src/libsystemd/test-bus-introspect.c
deleted file mode 100644
index 67b6461..0000000
--- a/src/libsystemd/test-bus-introspect.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-#include "log.h"
-#include "bus-introspect.h"
-
-static int prop_get(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
- return -EINVAL;
-}
-
-static int prop_set(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
- return -EINVAL;
-}
-
-static const sd_bus_vtable vtable[] = {
- SD_BUS_VTABLE_START(0),
- SD_BUS_METHOD("Hello", "ssas", "a(uu)", NULL, 0),
- SD_BUS_METHOD("DeprecatedHello", "", "", NULL, SD_BUS_VTABLE_DEPRECATED),
- SD_BUS_METHOD("DeprecatedHelloNoReply", "", "", NULL, SD_BUS_VTABLE_DEPRECATED|SD_BUS_VTABLE_METHOD_NO_REPLY),
- SD_BUS_SIGNAL("Wowza", "sss", 0),
- SD_BUS_SIGNAL("DeprecatedWowza", "ut", SD_BUS_VTABLE_DEPRECATED),
- SD_BUS_WRITABLE_PROPERTY("AProperty", "s", prop_get, prop_set, 0, 0),
- SD_BUS_PROPERTY("AReadOnlyDeprecatedProperty", "(ut)", prop_get, 0, SD_BUS_VTABLE_DEPRECATED),
- SD_BUS_PROPERTY("ChangingProperty", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
- SD_BUS_PROPERTY("Invalidating", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
- SD_BUS_PROPERTY("Constant", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_VTABLE_END
-};
-
-int main(int argc, char *argv[]) {
- struct introspect intro;
-
- log_set_max_level(LOG_DEBUG);
-
- assert_se(introspect_begin(&intro, false) >= 0);
-
- fprintf(intro.f, " <interface name=\"org.foo\">\n");
- assert_se(introspect_write_interface(&intro, vtable) >= 0);
- fputs(" </interface>\n", intro.f);
-
- fflush(intro.f);
- fputs(intro.introspection, stdout);
-
- introspect_free(&intro);
-
- return 0;
-}
diff --git a/src/libsystemd/test-bus-kernel-benchmark.c b/src/libsystemd/test-bus-kernel-benchmark.c
deleted file mode 100644
index 04627d9..0000000
--- a/src/libsystemd/test-bus-kernel-benchmark.c
+++ /dev/null
@@ -1,303 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <ctype.h>
-#include <sys/wait.h>
-
-#include "util.h"
-#include "log.h"
-#include "time-util.h"
-
-#include "sd-bus.h"
-#include "bus-message.h"
-#include "bus-error.h"
-#include "bus-kernel.h"
-#include "bus-internal.h"
-#include "bus-util.h"
-
-#define MAX_SIZE (4*1024*1024)
-
-static usec_t arg_loop_usec = 100 * USEC_PER_MSEC;
-
-static void server(sd_bus *b, size_t *result) {
- int r;
-
- for (;;) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-
- r = sd_bus_process(b, &m);
- assert_se(r >= 0);
-
- if (r == 0)
- assert_se(sd_bus_wait(b, (usec_t) -1) >= 0);
- if (!m)
- continue;
-
- if (sd_bus_message_is_method_call(m, "benchmark.server", "Ping"))
- assert_se(sd_bus_reply_method_return(m, NULL) >= 0);
- else if (sd_bus_message_is_method_call(m, "benchmark.server", "Work")) {
- const void *p;
- size_t sz;
-
- /* Make sure the mmap is mapped */
- assert_se(sd_bus_message_read_array(m, 'y', &p, &sz) > 0);
-
- assert_se(sd_bus_reply_method_return(m, NULL) >= 0);
- } else if (sd_bus_message_is_method_call(m, "benchmark.server", "Exit")) {
- uint64_t res;
- assert_se(sd_bus_message_read(m, "t", &res) > 0);
-
- *result = res;
- return;
-
- } else
- assert_not_reached("Unknown method");
- }
-}
-
-static void transaction(sd_bus *b, size_t sz) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
- uint8_t *p;
-
- assert_se(sd_bus_message_new_method_call(b, ":1.1", "/", "benchmark.server", "Work", &m) >= 0);
- assert_se(sd_bus_message_append_array_space(m, 'y', sz, (void**) &p) >= 0);
-
- memset(p, 0x80, sz);
-
- assert_se(sd_bus_call(b, m, 0, NULL, &reply) >= 0);
-}
-
-static void client_bisect(const char *address) {
- _cleanup_bus_message_unref_ sd_bus_message *x = NULL;
- size_t lsize, rsize, csize;
- sd_bus *b;
- int r;
-
- r = sd_bus_new(&b);
- assert_se(r >= 0);
-
- r = sd_bus_set_address(b, address);
- assert_se(r >= 0);
-
- r = sd_bus_start(b);
- assert_se(r >= 0);
-
- assert_se(sd_bus_call_method(b, ":1.1", "/", "benchmark.server", "Ping", NULL, NULL, NULL) >= 0);
-
- lsize = 1;
- rsize = MAX_SIZE;
-
- printf("SIZE\tCOPY\tMEMFD\n");
-
- for (;;) {
- usec_t t;
- unsigned n_copying, n_memfd;
-
- csize = (lsize + rsize) / 2;
-
- if (csize <= lsize)
- break;
-
- if (csize <= 0)
- break;
-
- printf("%zu\t", csize);
-
- b->use_memfd = 0;
-
- t = now(CLOCK_MONOTONIC);
- for (n_copying = 0;; n_copying++) {
- transaction(b, csize);
- if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
- break;
- }
- printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec));
-
- b->use_memfd = -1;
-
- t = now(CLOCK_MONOTONIC);
- for (n_memfd = 0;; n_memfd++) {
- transaction(b, csize);
- if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
- break;
- }
- printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec));
-
- if (n_copying == n_memfd)
- break;
-
- if (n_copying > n_memfd)
- lsize = csize;
- else
- rsize = csize;
- }
-
- b->use_memfd = 1;
- assert_se(sd_bus_message_new_method_call(b, ":1.1", "/", "benchmark.server", "Exit", &x) >= 0);
- assert_se(sd_bus_message_append(x, "t", csize) >= 0);
- assert_se(sd_bus_send(b, x, NULL) >= 0);
-
- sd_bus_unref(b);
-}
-
-static void client_chart(const char *address) {
- _cleanup_bus_message_unref_ sd_bus_message *x = NULL;
- size_t csize;
- sd_bus *b;
- int r;
-
- r = sd_bus_new(&b);
- assert_se(r >= 0);
-
- r = sd_bus_set_address(b, address);
- assert_se(r >= 0);
-
- r = sd_bus_start(b);
- assert_se(r >= 0);
-
- assert_se(sd_bus_call_method(b, ":1.1", "/", "benchmark.server", "Ping", NULL, NULL, NULL) >= 0);
-
- printf("SIZE\tCOPY\tMEMFD\n");
-
- for (csize = 1; csize <= MAX_SIZE; csize *= 2) {
- usec_t t;
- unsigned n_copying, n_memfd;
-
- printf("%zu\t", csize);
-
- b->use_memfd = 0;
-
- t = now(CLOCK_MONOTONIC);
- for (n_copying = 0;; n_copying++) {
- transaction(b, csize);
- if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
- break;
- }
-
- printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec));
-
- b->use_memfd = -1;
-
- t = now(CLOCK_MONOTONIC);
- for (n_memfd = 0;; n_memfd++) {
- transaction(b, csize);
- if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
- break;
- }
-
- printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec));
- }
-
- b->use_memfd = 1;
- assert_se(sd_bus_message_new_method_call(b, ":1.1", "/", "benchmark.server", "Exit", &x) >= 0);
- assert_se(sd_bus_message_append(x, "t", csize) >= 0);
- assert_se(sd_bus_send(b, x, NULL) >= 0);
-
- sd_bus_unref(b);
-}
-
-int main(int argc, char *argv[]) {
- enum {
- MODE_BISECT,
- MODE_CHART,
- } mode = MODE_BISECT;
- int i;
- _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL;
- _cleanup_close_ int bus_ref = -1;
- cpu_set_t cpuset;
- size_t result;
- sd_bus *b;
- pid_t pid;
- int r;
-
- for (i = 1; i < argc; i++) {
- if (streq(argv[i], "chart")) {
- mode = MODE_CHART;
- continue;
- }
-
- assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0);
- }
-
- assert_se(arg_loop_usec > 0);
-
- assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
-
- bus_ref = bus_kernel_create_bus(name, false, &bus_name);
- if (bus_ref == -ENOENT)
- exit(EXIT_TEST_SKIP);
-
- assert_se(bus_ref >= 0);
-
- address = strappend("kernel:path=", bus_name);
- assert_se(address);
-
- r = sd_bus_new(&b);
- assert_se(r >= 0);
-
- r = sd_bus_set_address(b, address);
- assert_se(r >= 0);
-
- r = sd_bus_start(b);
- assert_se(r >= 0);
-
- sync();
- setpriority(PRIO_PROCESS, 0, -19);
-
- pid = fork();
- assert_se(pid >= 0);
-
- if (pid == 0) {
- CPU_ZERO(&cpuset);
- CPU_SET(0, &cpuset);
- pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
-
- close_nointr_nofail(bus_ref);
- sd_bus_unref(b);
-
- switch (mode) {
- case MODE_BISECT:
- client_bisect(address);
- break;
-
- case MODE_CHART:
- client_chart(address);
- break;
- }
-
- _exit(0);
- }
-
- CPU_ZERO(&cpuset);
- CPU_SET(1, &cpuset);
- pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
-
- server(b, &result);
-
- if (mode == MODE_BISECT)
- printf("Copying/memfd are equally fast at %zu bytes\n", result);
-
- assert_se(waitpid(pid, NULL, 0) == pid);
-
- sd_bus_unref(b);
-
- return 0;
-}
diff --git a/src/libsystemd/test-bus-kernel-bloom.c b/src/libsystemd/test-bus-kernel-bloom.c
deleted file mode 100644
index 0ecad18..0000000
--- a/src/libsystemd/test-bus-kernel-bloom.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-#include "log.h"
-
-#include "sd-bus.h"
-#include "bus-message.h"
-#include "bus-error.h"
-#include "bus-kernel.h"
-#include "bus-util.h"
-
-static void test_one(
- const char *path,
- const char *interface,
- const char *member,
- const char *arg0,
- const char *match,
- bool good) {
-
- _cleanup_close_ int bus_ref = -1;
- _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- sd_bus *a, *b;
- int r;
-
- assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
-
- bus_ref = bus_kernel_create_bus(name, false, &bus_name);
- if (bus_ref == -ENOENT)
- exit(EXIT_TEST_SKIP);
-
- assert_se(bus_ref >= 0);
-
- address = strappend("kernel:path=", bus_name);
- assert_se(address);
-
- r = sd_bus_new(&a);
- assert_se(r >= 0);
-
- r = sd_bus_new(&b);
- assert_se(r >= 0);
-
- r = sd_bus_set_address(a, address);
- assert_se(r >= 0);
-
- r = sd_bus_set_address(b, address);
- assert_se(r >= 0);
-
- r = sd_bus_start(a);
- assert_se(r >= 0);
-
- r = sd_bus_start(b);
- assert_se(r >= 0);
-
- log_debug("match");
- r = sd_bus_add_match(b, match, NULL, NULL);
- assert_se(r >= 0);
-
- log_debug("signal");
- r = sd_bus_emit_signal(a, path, interface, member, "s", arg0);
- assert_se(r >= 0);
-
- r = sd_bus_process(b, &m);
- assert_se(r >= 0 && (good == !!m));
-
- sd_bus_unref(a);
- sd_bus_unref(b);
-}
-
-int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
-
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "", true);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo'", true);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo/tuut'", false);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "interface='waldo.com'", true);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "member='Piep'", true);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "member='Pi_ep'", false);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "arg0='foobar'", true);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "arg0='foo_bar'", false);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar'", true);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar2'", false);
-
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo'", true);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar'", false);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo'", false);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/'", false);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo/quux'", false);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/foo/bar/waldo'", true);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/foo/bar'", true);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/foo'", true);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/'", true);
- test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/quux'", false);
-
- return 0;
-}
diff --git a/src/libsystemd/test-bus-kernel.c b/src/libsystemd/test-bus-kernel.c
deleted file mode 100644
index 9b17a35..0000000
--- a/src/libsystemd/test-bus-kernel.c
+++ /dev/null
@@ -1,175 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <fcntl.h>
-
-#include "util.h"
-#include "log.h"
-
-#include "sd-bus.h"
-#include "bus-message.h"
-#include "bus-error.h"
-#include "bus-kernel.h"
-#include "bus-util.h"
-#include "bus-dump.h"
-
-int main(int argc, char *argv[]) {
- _cleanup_close_ int bus_ref = -1;
- _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- const char *ua = NULL, *ub = NULL, *the_string = NULL;
- sd_bus *a, *b;
- int r, pipe_fds[2];
-
- log_set_max_level(LOG_DEBUG);
-
- assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
-
- bus_ref = bus_kernel_create_bus(name, false, &bus_name);
- if (bus_ref == -ENOENT)
- return EXIT_TEST_SKIP;
-
- assert_se(bus_ref >= 0);
-
- address = strappend("kernel:path=", bus_name);
- assert_se(address);
-
- r = sd_bus_new(&a);
- assert_se(r >= 0);
-
- r = sd_bus_new(&b);
- assert_se(r >= 0);
-
- r = sd_bus_set_address(a, address);
- assert_se(r >= 0);
-
- r = sd_bus_set_address(b, address);
- assert_se(r >= 0);
-
- assert_se(sd_bus_negotiate_attach_timestamp(a, 1) >= 0);
- assert_se(sd_bus_negotiate_attach_creds(a, _SD_BUS_CREDS_ALL) >= 0);
-
- assert_se(sd_bus_negotiate_attach_timestamp(b, 1) >= 0);
- assert_se(sd_bus_negotiate_attach_creds(b, _SD_BUS_CREDS_ALL) >= 0);
-
- r = sd_bus_start(a);
- assert_se(r >= 0);
-
- r = sd_bus_start(b);
- assert_se(r >= 0);
-
- r = sd_bus_get_unique_name(a, &ua);
- assert_se(r >= 0);
-
- printf("unique a: %s\n", ua);
-
- r = sd_bus_get_unique_name(b, &ub);
- assert_se(r >= 0);
-
- printf("unique b: %s\n", ub);
-
- r = sd_bus_call_method(a, "this.doesnt.exist", "/foo", "meh.mah", "muh", &error, NULL, "s", "yayayay");
- assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_SERVICE_UNKNOWN));
- assert_se(r == -EHOSTUNREACH);
-
- r = sd_bus_add_match(b, "interface='waldo.com',member='Piep'", NULL, NULL);
- assert_se(r >= 0);
-
- r = sd_bus_emit_signal(a, "/foo/bar/waldo", "waldo.com", "Piep", "sss", "I am a string", "/this/is/a/path", "and.this.a.domain.name");
- assert_se(r >= 0);
-
- r = sd_bus_try_close(b);
- assert_se(r == -EBUSY);
-
- r = sd_bus_process(b, &m);
- assert_se(r > 0);
- assert_se(m);
-
- bus_message_dump(m, stdout, true);
- assert_se(sd_bus_message_rewind(m, true) >= 0);
-
- r = sd_bus_message_read(m, "s", &the_string);
- assert_se(r >= 0);
- assert_se(streq(the_string, "I am a string"));
-
- sd_bus_message_unref(m);
- m = NULL;
-
- r = sd_bus_request_name(a, "net.x0pointer.foobar", 0);
- assert_se(r >= 0);
-
- r = sd_bus_message_new_method_call(b, "net.x0pointer.foobar", "/a/path", "an.inter.face", "AMethod", &m);
- assert_se(r >= 0);
-
- assert_se(pipe2(pipe_fds, O_CLOEXEC) >= 0);
-
- assert_se(write(pipe_fds[1], "x", 1) == 1);
-
- close_nointr_nofail(pipe_fds[1]);
- pipe_fds[1] = -1;
-
- r = sd_bus_message_append(m, "h", pipe_fds[0]);
- assert_se(r >= 0);
-
- close_nointr_nofail(pipe_fds[0]);
- pipe_fds[0] = -1;
-
- r = sd_bus_send(b, m, NULL);
- assert_se(r >= 0);
-
- for (;;) {
- sd_bus_message_unref(m);
- m = NULL;
- r = sd_bus_process(a, &m);
- assert_se(r > 0);
- assert_se(m);
-
- bus_message_dump(m, stdout, true);
- assert_se(sd_bus_message_rewind(m, true) >= 0);
-
- if (sd_bus_message_is_method_call(m, "an.inter.face", "AMethod")) {
- int fd;
- char x;
-
- r = sd_bus_message_read(m, "h", &fd);
- assert_se(r >= 0);
-
- assert_se(read(fd, &x, 1) == 1);
- assert_se(x == 'x');
- break;
- }
- }
-
- r = sd_bus_release_name(a, "net.x0pointer.foobar");
- assert_se(r >= 0);
-
- r = sd_bus_release_name(a, "net.x0pointer.foobar");
- assert_se(r == -ESRCH);
-
- r = sd_bus_try_close(a);
- assert_se(r >= 0);
-
- sd_bus_unref(a);
- sd_bus_unref(b);
-
- return 0;
-}
diff --git a/src/libsystemd/test-bus-marshal.c b/src/libsystemd/test-bus-marshal.c
deleted file mode 100644
index 4fad049..0000000
--- a/src/libsystemd/test-bus-marshal.c
+++ /dev/null
@@ -1,324 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <stdlib.h>
-#include <byteswap.h>
-
-#ifdef HAVE_GLIB
-#include <gio/gio.h>
-#endif
-
-#ifdef HAVE_DBUS
-#include <dbus/dbus.h>
-#endif
-
-#include "log.h"
-#include "util.h"
-
-#include "sd-bus.h"
-#include "bus-message.h"
-#include "bus-util.h"
-#include "bus-dump.h"
-
-static void test_bus_label_escape_one(const char *a, const char *b) {
- _cleanup_free_ char *t = NULL, *x = NULL, *y = NULL;
-
- assert_se(t = sd_bus_label_escape(a));
- assert_se(streq(t, b));
-
- assert_se(x = sd_bus_label_unescape(t));
- assert_se(streq(a, x));
-
- assert_se(y = sd_bus_label_unescape(b));
- assert_se(streq(a, y));
-}
-
-static void test_bus_label_escape(void) {
- test_bus_label_escape_one("foo123bar", "foo123bar");
- test_bus_label_escape_one("foo.bar", "foo_2ebar");
- test_bus_label_escape_one("foo_2ebar", "foo_5f2ebar");
- test_bus_label_escape_one("", "_");
- test_bus_label_escape_one("_", "_5f");
- test_bus_label_escape_one("1", "_31");
- test_bus_label_escape_one(":1", "_3a1");
-}
-
-int main(int argc, char *argv[]) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *copy = NULL;
- int r, boolean;
- const char *x, *x2, *y, *z, *a, *b, *c, *d, *a_signature;
- uint8_t u, v;
- void *buffer = NULL;
- size_t sz;
- char *h;
- const int32_t integer_array[] = { -1, -2, 0, 1, 2 }, *return_array;
- char *s;
- _cleanup_free_ char *first = NULL, *second = NULL, *third = NULL;
- _cleanup_fclose_ FILE *ms = NULL;
- size_t first_size = 0, second_size = 0, third_size = 0;
-
- r = sd_bus_message_new_method_call(NULL, "foobar.waldo", "/", "foobar.waldo", "Piep", &m);
- assert_se(r >= 0);
-
- r = sd_bus_message_append(m, "s", "a string");
- assert_se(r >= 0);
-
- r = sd_bus_message_append(m, "s", NULL);
- assert_se(r >= 0);
-
- r = sd_bus_message_append(m, "asg", 2, "string #1", "string #2", "sba(tt)ss");
- assert_se(r >= 0);
-
- r = sd_bus_message_append(m, "sass", "foobar", 5, "foo", "bar", "waldo", "piep", "pap", "after");
- assert_se(r >= 0);
-
- r = sd_bus_message_append(m, "a{yv}", 2, 3, "s", "foo", 5, "s", "waldo");
- assert_se(r >= 0);
-
- r = sd_bus_message_append(m, "ba(ss)", 255, 3, "aaa", "1", "bbb", "2", "ccc", "3");
- assert_se(r >= 0);
-
- r = sd_bus_message_open_container(m, 'a', "s");
- assert_se(r >= 0);
-
- r = sd_bus_message_append_basic(m, 's', "foobar");
- assert_se(r >= 0);
-
- r = sd_bus_message_append_basic(m, 's', "waldo");
- assert_se(r >= 0);
-
- r = sd_bus_message_close_container(m);
- assert_se(r >= 0);
-
- r = sd_bus_message_append_string_space(m, 5, &s);
- assert_se(r >= 0);
- strcpy(s, "hallo");
-
- r = sd_bus_message_append_array(m, 'i', integer_array, sizeof(integer_array));
- assert_se(r >= 0);
-
- r = sd_bus_message_append_array(m, 'u', NULL, 0);
- assert_se(r >= 0);
-
- r = bus_message_seal(m, 4711, 0);
- assert_se(r >= 0);
-
- bus_message_dump(m, stdout, true);
-
- ms = open_memstream(&first, &first_size);
- bus_message_dump(m, ms, false);
- fflush(ms);
- assert_se(!ferror(ms));
-
- r = bus_message_get_blob(m, &buffer, &sz);
- assert_se(r >= 0);
-
- h = hexmem(buffer, sz);
- assert_se(h);
-
- log_info("message size = %lu, contents =\n%s", (unsigned long) sz, h);
- free(h);
-
-#ifdef HAVE_GLIB
- {
- GDBusMessage *g;
- char *p;
-
-#if !defined(GLIB_VERSION_2_36)
- g_type_init();
-#endif
-
- g = g_dbus_message_new_from_blob(buffer, sz, 0, NULL);
- p = g_dbus_message_print(g, 0);
- log_info("%s", p);
- g_free(p);
- g_object_unref(g);
- }
-#endif
-
-#ifdef HAVE_DBUS
- {
- DBusMessage *w;
- DBusError error;
-
- dbus_error_init(&error);
-
- w = dbus_message_demarshal(buffer, sz, &error);
- if (!w)
- log_error("%s", error.message);
- else
- dbus_message_unref(w);
- }
-#endif
-
- m = sd_bus_message_unref(m);
-
- r = bus_message_from_malloc(NULL, buffer, sz, NULL, 0, NULL, NULL, &m);
- assert_se(r >= 0);
-
- bus_message_dump(m, stdout, true);
-
- fclose(ms);
- ms = open_memstream(&second, &second_size);
- bus_message_dump(m, ms, false);
- fflush(ms);
- assert_se(!ferror(ms));
- assert_se(first_size == second_size);
- assert_se(memcmp(first, second, first_size) == 0);
-
- assert_se(sd_bus_message_rewind(m, true) >= 0);
-
- r = sd_bus_message_read(m, "ssasg", &x, &x2, 2, &y, &z, &a_signature);
- assert_se(r > 0);
- assert_se(streq(x, "a string"));
- assert_se(streq(x2, ""));
- assert_se(streq(y, "string #1"));
- assert_se(streq(z, "string #2"));
- assert_se(streq(a_signature, "sba(tt)ss"));
-
- r = sd_bus_message_read(m, "sass", &x, 5, &y, &z, &a, &b, &c, &d);
- assert_se(r > 0);
- assert_se(streq(x, "foobar"));
- assert_se(streq(y, "foo"));
- assert_se(streq(z, "bar"));
- assert_se(streq(a, "waldo"));
- assert_se(streq(b, "piep"));
- assert_se(streq(c, "pap"));
- assert_se(streq(d, "after"));
-
- r = sd_bus_message_read(m, "a{yv}", 2, &u, "s", &x, &v, "s", &y);
- assert_se(r > 0);
- assert_se(u == 3);
- assert_se(streq(x, "foo"));
- assert_se(v == 5);
- assert_se(streq(y, "waldo"));
-
- r = sd_bus_message_read(m, "ba(ss)", &boolean, 3, &x, &y, &a, &b, &c, &d);
- assert_se(r > 0);
- assert_se(boolean);
- assert_se(streq(x, "aaa"));
- assert_se(streq(y, "1"));
- assert_se(streq(a, "bbb"));
- assert_se(streq(b, "2"));
- assert_se(streq(c, "ccc"));
- assert_se(streq(d, "3"));
-
- assert_se(sd_bus_message_verify_type(m, 'a', "s") > 0);
-
- r = sd_bus_message_read(m, "as", 2, &x, &y);
- assert_se(r > 0);
- assert_se(streq(x, "foobar"));
- assert_se(streq(y, "waldo"));
-
- r = sd_bus_message_read_basic(m, 's', &s);
- assert_se(r > 0);
- assert_se(streq(s, "hallo"));
-
- r = sd_bus_message_read_array(m, 'i', (const void**) &return_array, &sz);
- assert_se(r > 0);
- assert_se(sz == sizeof(integer_array));
- assert_se(memcmp(integer_array, return_array, sz) == 0);
-
- r = sd_bus_message_read_array(m, 'u', (const void**) &return_array, &sz);
- assert_se(r > 0);
- assert_se(sz == 0);
-
- r = sd_bus_message_peek_type(m, NULL, NULL);
- assert_se(r == 0);
-
- r = sd_bus_message_new_method_call(NULL, "foobar.waldo", "/", "foobar.waldo", "Piep", ©);
- assert_se(r >= 0);
-
- r = sd_bus_message_rewind(m, true);
- assert_se(r >= 0);
-
- r = sd_bus_message_copy(copy, m, true);
- assert_se(r >= 0);
-
- r = bus_message_seal(copy, 4712, 0);
- assert_se(r >= 0);
-
- fclose(ms);
- ms = open_memstream(&third, &third_size);
- bus_message_dump(copy, ms, false);
- fflush(ms);
- assert_se(!ferror(ms));
-
- printf("<%.*s>\n", (int) first_size, first);
- printf("<%.*s>\n", (int) third_size, third);
-
- assert_se(first_size == third_size);
- assert_se(memcmp(first, third, third_size) == 0);
-
- r = sd_bus_message_rewind(m, true);
- assert_se(r >= 0);
-
- assert_se(sd_bus_message_verify_type(m, 's', NULL) > 0);
-
- r = sd_bus_message_skip(m, "ssasg");
- assert_se(r > 0);
-
- assert_se(sd_bus_message_verify_type(m, 's', NULL) > 0);
-
- r = sd_bus_message_skip(m, "sass");
- assert_se(r >= 0);
-
- assert_se(sd_bus_message_verify_type(m, 'a', "{yv}") > 0);
-
- r = sd_bus_message_skip(m, "a{yv}");
- assert_se(r >= 0);
-
- assert_se(sd_bus_message_verify_type(m, 'b', NULL) > 0);
-
- r = sd_bus_message_read(m, "b", &boolean);
- assert_se(r > 0);
- assert_se(boolean);
-
- r = sd_bus_message_enter_container(m, 0, NULL);
- assert_se(r > 0);
-
- r = sd_bus_message_read(m, "(ss)", &x, &y);
- assert_se(r > 0);
-
- r = sd_bus_message_read(m, "(ss)", &a, &b);
- assert_se(r > 0);
-
- r = sd_bus_message_read(m, "(ss)", &c, &d);
- assert_se(r > 0);
-
- r = sd_bus_message_read(m, "(ss)", &x, &y);
- assert_se(r == 0);
-
- r = sd_bus_message_exit_container(m);
- assert_se(r >= 0);
-
- assert_se(streq(x, "aaa"));
- assert_se(streq(y, "1"));
- assert_se(streq(a, "bbb"));
- assert_se(streq(b, "2"));
- assert_se(streq(c, "ccc"));
- assert_se(streq(d, "3"));
-
- test_bus_label_escape();
-
- return 0;
-}
diff --git a/src/libsystemd/test-bus-match.c b/src/libsystemd/test-bus-match.c
deleted file mode 100644
index 7227e25..0000000
--- a/src/libsystemd/test-bus-match.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-
-#include "log.h"
-#include "util.h"
-#include "macro.h"
-
-#include "bus-match.h"
-#include "bus-message.h"
-#include "bus-util.h"
-
-static bool mask[32];
-
-static int filter(sd_bus *b, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
- log_info("Ran %i", PTR_TO_INT(userdata));
- mask[PTR_TO_INT(userdata)] = true;
- return 0;
-}
-
-static bool mask_contains(unsigned a[], unsigned n) {
- unsigned i, j;
-
- for (i = 0; i < ELEMENTSOF(mask); i++) {
- bool found = false;
-
- for (j = 0; j < n; j++)
- if (a[j] == i) {
- found = true;
- break;
- }
-
- if (found != mask[i])
- return false;
- }
-
- return true;
-}
-
-static int match_add(struct bus_match_node *root, const char *match, int value) {
- struct bus_match_component *components = NULL;
- unsigned n_components = 0;
- int r;
-
- r = bus_match_parse(match, &components, &n_components);
- if (r < 0)
- return r;
-
- r = bus_match_add(root, components, n_components, filter, INT_TO_PTR(value), 0, NULL);
- bus_match_parse_free(components, n_components);
-
- return r;
-}
-
-static int match_remove(struct bus_match_node *root, const char *match, int value) {
- struct bus_match_component *components = NULL;
- unsigned n_components = 0;
- int r;
-
- r = bus_match_parse(match, &components, &n_components);
- if (r < 0)
- return r;
-
- r = bus_match_remove(root, components, n_components, filter, INT_TO_PTR(value), 0);
- bus_match_parse_free(components, n_components);
-
- return r;
-}
-
-int main(int argc, char *argv[]) {
- struct bus_match_node root;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- enum bus_match_node_type i;
-
- zero(root);
- root.type = BUS_MATCH_ROOT;
-
- assert_se(match_add(&root, "arg2='wal\\'do',sender='foo',type='signal',interface='bar.x',", 1) >= 0);
- assert_se(match_add(&root, "arg2='wal\\'do2',sender='foo',type='signal',interface='bar.x',", 2) >= 0);
- assert_se(match_add(&root, "arg3='test',sender='foo',type='signal',interface='bar.x',", 3) >= 0);
- assert_se(match_add(&root, "arg3='test',sender='foo',type='method_call',interface='bar.x',", 4) >= 0);
- assert_se(match_add(&root, "", 5) >= 0);
- assert_se(match_add(&root, "interface='quux.x'", 6) >= 0);
- assert_se(match_add(&root, "interface='bar.x'", 7) >= 0);
- assert_se(match_add(&root, "member='waldo',path='/foo/bar'", 8) >= 0);
- assert_se(match_add(&root, "path='/foo/bar'", 9) >= 0);
- assert_se(match_add(&root, "path_namespace='/foo'", 10) >= 0);
- assert_se(match_add(&root, "path_namespace='/foo/quux'", 11) >= 0);
- assert_se(match_add(&root, "arg1='two'", 12) >= 0);
- assert_se(match_add(&root, "member='waldo',arg2path='/prefix/'", 13) >= 0);
- assert_se(match_add(&root, "member=waldo,path='/foo/bar',arg3namespace='prefix'", 14) >= 0);
-
- bus_match_dump(&root, 0);
-
- assert_se(sd_bus_message_new_signal(NULL, "/foo/bar", "bar.x", "waldo", &m) >= 0);
- assert_se(sd_bus_message_append(m, "ssss", "one", "two", "/prefix/three", "prefix.four") >= 0);
- assert_se(bus_message_seal(m, 1, 0) >= 0);
-
- zero(mask);
- assert_se(bus_match_run(NULL, &root, m) == 0);
- assert_se(mask_contains((unsigned[]) { 9, 8, 7, 5, 10, 12, 13, 14 }, 8));
-
- assert_se(match_remove(&root, "member='waldo',path='/foo/bar'", 8) > 0);
- assert_se(match_remove(&root, "arg2path='/prefix/',member='waldo'", 13) > 0);
- assert_se(match_remove(&root, "interface='bar.xx'", 7) == 0);
-
- bus_match_dump(&root, 0);
-
- zero(mask);
- assert_se(bus_match_run(NULL, &root, m) == 0);
- assert_se(mask_contains((unsigned[]) { 9, 5, 10, 12, 14, 7 }, 6));
-
- for (i = 0; i < _BUS_MATCH_NODE_TYPE_MAX; i++) {
- char buf[32];
- const char *x;
-
- assert_se(x = bus_match_node_type_to_string(i, buf, sizeof(buf)));
-
- if (i >= BUS_MATCH_MESSAGE_TYPE)
- assert_se(bus_match_node_type_from_string(x, strlen(x)) == i);
- }
-
- bus_match_free(&root);
-
- return 0;
-}
diff --git a/src/libsystemd/test-bus-memfd.c b/src/libsystemd/test-bus-memfd.c
deleted file mode 100644
index b9d6a25..0000000
--- a/src/libsystemd/test-bus-memfd.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/mman.h>
-#include <sys/uio.h>
-
-#include "log.h"
-#include "macro.h"
-#include "util.h"
-
-#include "sd-memfd.h"
-
-int main(int argc, char *argv[]) {
- sd_memfd *m;
- char *s;
- uint64_t sz;
- int r, fd;
- FILE *f;
- char buf[3] = {};
- struct iovec iov[3] = {};
- char bufv[3][3] = {};
-
- log_set_max_level(LOG_DEBUG);
-
- r = sd_memfd_new(&m);
- if (r == -ENOENT)
- return EXIT_TEST_SKIP;
-
- r = sd_memfd_map(m, 0, 12, (void**) &s);
- assert_se(r >= 0);
-
- strcpy(s, "----- world");
-
- r = sd_memfd_set_sealed(m, 1);
- assert_se(r == -ETXTBSY);
-
- assert_se(write(sd_memfd_get_fd(m), "he", 2) == 2);
- assert_se(write(sd_memfd_get_fd(m), "XXX", 3) == 3);
- assert_se(streq(s, "heXXX world"));
-
- /* fix "hello" */
- assert_se(lseek(sd_memfd_get_fd(m), 2, SEEK_SET) == 2);
- assert_se(write(sd_memfd_get_fd(m), "ll", 2) == 2);
-
- assert_se(sd_memfd_get_file(m, &f) >= 0);
- fputc('o', f);
- fflush(f);
-
- /* check content */
- assert_se(streq(s, "hello world"));
-
- assert_se(munmap(s, 12) == 0);
-
- r = sd_memfd_get_sealed(m);
- assert_se(r == 0);
-
- r = sd_memfd_get_size(m, &sz);
- assert_se(r >= 0);
- assert_se(sz = page_size());
-
- /* truncate it */
- r = sd_memfd_set_size(m, 6);
- assert_se(r >= 0);
-
- /* get back new value */
- r = sd_memfd_get_size(m, &sz);
- assert_se(r >= 0);
- assert_se(sz == 6);
-
- r = sd_memfd_set_sealed(m, 1);
- assert_se(r >= 0);
-
- r = sd_memfd_get_sealed(m);
- assert_se(r == 1);
-
- fd = sd_memfd_dup_fd(m);
- assert_se(fd >= 0);
-
- sd_memfd_free(m);
-
- /* new sd_memfd, same underlying memfd */
- r = sd_memfd_make(fd, &m);
- assert_se(r >= 0);
-
- /* we did truncate it to 6 */
- r = sd_memfd_get_size(m, &sz);
- assert_se(r >= 0 && sz == 6);
-
- /* map it, check content */
- r = sd_memfd_map(m, 0, 12, (void **)&s);
- assert_se(r >= 0);
-
- /* we only see the truncated size */
- assert_se(streq(s, "hello "));
-
- /* it was already sealed */
- r = sd_memfd_set_sealed(m, 1);
- assert_se(r == -EALREADY);
-
- /* we cannot break the seal, it is mapped */
- r = sd_memfd_set_sealed(m, 0);
- assert_se(r == -ETXTBSY);
-
- /* unmap it; become the single owner */
- assert_se(munmap(s, 12) == 0);
-
- /* now we can do flip the sealing */
- r = sd_memfd_set_sealed(m, 0);
- assert_se(r == 0);
- r = sd_memfd_get_sealed(m);
- assert_se(r == 0);
-
- r = sd_memfd_set_sealed(m, 1);
- assert_se(r == 0);
- r = sd_memfd_get_sealed(m);
- assert_se(r == 1);
-
- r = sd_memfd_set_sealed(m, 0);
- assert_se(r == 0);
- r = sd_memfd_get_sealed(m);
- assert_se(r == 0);
-
- /* seek at 2, read() 2 bytes */
- assert_se(lseek(fd, 2, SEEK_SET) == 2);
- assert_se(read(fd, buf, 2) == 2);
-
- /* check content */
- assert_se(memcmp(buf, "ll", 2) == 0);
-
- /* writev it out*/
- iov[0].iov_base = (char *)"ABC";
- iov[0].iov_len = 3;
- iov[1].iov_base = (char *)"DEF";
- iov[1].iov_len = 3;
- iov[2].iov_base = (char *)"GHI";
- iov[2].iov_len = 3;
- assert_se(pwritev(fd, iov, 3, 0) == 9);
-
- /* readv it back */
- iov[0].iov_base = bufv[0];
- iov[0].iov_len = 3;
- iov[1].iov_base = bufv[1];
- iov[1].iov_len = 3;
- iov[2].iov_base = bufv[2];
- iov[2].iov_len = 3;
- assert_se(preadv(fd, iov, 3, 0) == 9);
-
- /* check content */
- assert_se(memcmp(bufv[0], "ABC", 3) == 0);
- assert_se(memcmp(bufv[1], "DEF", 3) == 0);
- assert_se(memcmp(bufv[2], "GHI", 3) == 0);
-
- sd_memfd_free(m);
-
- return 0;
-}
diff --git a/src/libsystemd/test-bus-objects.c b/src/libsystemd/test-bus-objects.c
deleted file mode 100644
index e2423c7..0000000
--- a/src/libsystemd/test-bus-objects.c
+++ /dev/null
@@ -1,500 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "log.h"
-#include "util.h"
-#include "macro.h"
-#include "strv.h"
-
-#include "sd-bus.h"
-#include "bus-internal.h"
-#include "bus-message.h"
-#include "bus-util.h"
-#include "bus-dump.h"
-
-struct context {
- int fds[2];
- bool quit;
- char *something;
- char *automatic_string_property;
- uint32_t automatic_integer_property;
-};
-
-static int something_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- struct context *c = userdata;
- const char *s;
- char *n = NULL;
- int r;
-
- r = sd_bus_message_read(m, "s", &s);
- assert_se(r > 0);
-
- n = strjoin("<<<", s, ">>>", NULL);
- assert_se(n);
-
- free(c->something);
- c->something = n;
-
- log_info("AlterSomething() called, got %s, returning %s", s, n);
-
- /* This should fail, since the return type doesn't match */
- assert_se(sd_bus_reply_method_return(m, "u", 4711) == -ENOMSG);
-
- r = sd_bus_reply_method_return(m, "s", n);
- assert_se(r >= 0);
-
- return 1;
-}
-
-static int exit_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- struct context *c = userdata;
- int r;
-
- c->quit = true;
-
- log_info("Exit called");
-
- r = sd_bus_reply_method_return(m, "");
- assert_se(r >= 0);
-
- return 1;
-}
-
-static int get_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
- struct context *c = userdata;
- int r;
-
- log_info("property get for %s called, returning \"%s\".", property, c->something);
-
- r = sd_bus_message_append(reply, "s", c->something);
- assert_se(r >= 0);
-
- return 1;
-}
-
-static int set_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *error) {
- struct context *c = userdata;
- const char *s;
- char *n;
- int r;
-
- log_info("property set for %s called", property);
-
- r = sd_bus_message_read(value, "s", &s);
- assert_se(r >= 0);
-
- n = strdup(s);
- assert_se(n);
-
- free(c->something);
- c->something = n;
-
- return 1;
-}
-
-static int value_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
- _cleanup_free_ char *s = NULL;
- const char *x;
- int r;
-
- assert_se(asprintf(&s, "object %p, path %s", userdata, path) >= 0);
- r = sd_bus_message_append(reply, "s", s);
- assert_se(r >= 0);
-
- assert_se(x = startswith(path, "/value/"));
-
- assert_se(PTR_TO_UINT(userdata) == 30);
-
- return 1;
-}
-
-static int notify_test(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- int r;
-
- assert_se(sd_bus_emit_properties_changed(bus, m->path, "org.freedesktop.systemd.ValueTest", "Value", NULL) >= 0);
-
- r = sd_bus_reply_method_return(m, NULL);
- assert_se(r >= 0);
-
- return 1;
-}
-
-static int notify_test2(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- int r;
-
- assert_se(sd_bus_emit_properties_changed_strv(bus, m->path, "org.freedesktop.systemd.ValueTest", NULL) >= 0);
-
- r = sd_bus_reply_method_return(m, NULL);
- assert_se(r >= 0);
-
- return 1;
-}
-
-static int emit_interfaces_added(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- int r;
-
- assert_se(sd_bus_emit_interfaces_added(bus, m->path, "org.freedesktop.systemd.test", NULL) >= 0);
-
- r = sd_bus_reply_method_return(m, NULL);
- assert_se(r >= 0);
-
- return 1;
-}
-
-static int emit_interfaces_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- int r;
-
- assert_se(sd_bus_emit_interfaces_removed(bus, m->path, "org.freedesktop.systemd.test", NULL) >= 0);
-
- r = sd_bus_reply_method_return(m, NULL);
- assert_se(r >= 0);
-
- return 1;
-}
-
-static const sd_bus_vtable vtable[] = {
- SD_BUS_VTABLE_START(0),
- SD_BUS_METHOD("AlterSomething", "s", "s", something_handler, 0),
- SD_BUS_METHOD("Exit", "", "", exit_handler, 0),
- SD_BUS_WRITABLE_PROPERTY("Something", "s", get_handler, set_handler, 0, 0),
- SD_BUS_WRITABLE_PROPERTY("AutomaticStringProperty", "s", NULL, NULL, offsetof(struct context, automatic_string_property), 0),
- SD_BUS_WRITABLE_PROPERTY("AutomaticIntegerProperty", "u", NULL, NULL, offsetof(struct context, automatic_integer_property), 0),
- SD_BUS_METHOD("NoOperation", NULL, NULL, NULL, 0),
- SD_BUS_METHOD("EmitInterfacesAdded", NULL, NULL, emit_interfaces_added, 0),
- SD_BUS_METHOD("EmitInterfacesRemoved", NULL, NULL, emit_interfaces_removed, 0),
- SD_BUS_VTABLE_END
-};
-
-static const sd_bus_vtable vtable2[] = {
- SD_BUS_VTABLE_START(0),
- SD_BUS_METHOD("NotifyTest", "", "", notify_test, 0),
- SD_BUS_METHOD("NotifyTest2", "", "", notify_test2, 0),
- SD_BUS_PROPERTY("Value", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
- SD_BUS_PROPERTY("Value2", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
- SD_BUS_PROPERTY("Value3", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("Value4", "s", value_handler, 10, 0),
- SD_BUS_VTABLE_END
-};
-
-static int enumerator_callback(sd_bus *b, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
-
- if (object_path_startswith("/value", path))
- assert_se(*nodes = strv_new("/value/a", "/value/b", "/value/c", NULL));
-
- return 1;
-}
-
-static void *server(void *p) {
- struct context *c = p;
- sd_bus *bus = NULL;
- sd_id128_t id;
- int r;
-
- c->quit = false;
-
- assert_se(sd_id128_randomize(&id) >= 0);
-
- assert_se(sd_bus_new(&bus) >= 0);
- assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0);
- assert_se(sd_bus_set_server(bus, 1, id) >= 0);
-
- assert_se(sd_bus_add_object_vtable(bus, "/foo", "org.freedesktop.systemd.test", vtable, c) >= 0);
- assert_se(sd_bus_add_object_vtable(bus, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0);
- assert_se(sd_bus_add_fallback_vtable(bus, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0);
- assert_se(sd_bus_add_node_enumerator(bus, "/value", enumerator_callback, NULL) >= 0);
- assert_se(sd_bus_add_object_manager(bus, "/value") >= 0);
-
- assert_se(sd_bus_start(bus) >= 0);
-
- log_error("Entering event loop on server");
-
- while (!c->quit) {
- log_error("Loop!");
-
- r = sd_bus_process(bus, NULL);
- if (r < 0) {
- log_error("Failed to process requests: %s", strerror(-r));
- goto fail;
- }
-
- if (r == 0) {
- r = sd_bus_wait(bus, (uint64_t) -1);
- if (r < 0) {
- log_error("Failed to wait: %s", strerror(-r));
- goto fail;
- }
-
- continue;
- }
- }
-
- r = 0;
-
-fail:
- if (bus) {
- sd_bus_flush(bus);
- sd_bus_unref(bus);
- }
-
- return INT_TO_PTR(r);
-}
-
-static int client(struct context *c) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_unref_ sd_bus *bus = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- const char *s;
- int r;
-
- assert_se(sd_bus_new(&bus) >= 0);
- assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0);
- assert_se(sd_bus_start(bus) >= 0);
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "NoOperation", &error, NULL, NULL);
- assert_se(r >= 0);
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "s", "hallo");
- assert_se(r >= 0);
-
- r = sd_bus_message_read(reply, "s", &s);
- assert_se(r >= 0);
- assert_se(streq(s, "<<<hallo>>>"));
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Doesntexist", &error, &reply, "");
- assert_se(r < 0);
- assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD));
-
- sd_bus_error_free(&error);
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "as", 1, "hallo");
- assert_se(r < 0);
- assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS));
-
- sd_bus_error_free(&error);
-
- r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
- assert_se(r >= 0);
-
- r = sd_bus_message_read(reply, "s", &s);
- assert_se(r >= 0);
- assert_se(streq(s, "<<<hallo>>>"));
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, "s", "test");
- assert_se(r >= 0);
-
- r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
- assert_se(r >= 0);
-
- r = sd_bus_message_read(reply, "s", &s);
- assert_se(r >= 0);
- assert_se(streq(s, "test"));
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticIntegerProperty", &error, "u", 815);
- assert_se(r >= 0);
-
- assert_se(c->automatic_integer_property == 815);
-
- r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticStringProperty", &error, "s", "Du Dödel, Du!");
- assert_se(r >= 0);
-
- assert_se(streq(c->automatic_string_property, "Du Dödel, Du!"));
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
- assert_se(r >= 0);
-
- r = sd_bus_message_read(reply, "s", &s);
- assert_se(r >= 0);
- fputs(s, stdout);
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/value/xuzz", "org.freedesktop.systemd.ValueTest", "Value", &error, &reply, "s");
- assert_se(r >= 0);
-
- r = sd_bus_message_read(reply, "s", &s);
- assert_se(r >= 0);
- log_info("read %s", s);
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
- assert_se(r >= 0);
-
- r = sd_bus_message_read(reply, "s", &s);
- assert_se(r >= 0);
- fputs(s, stdout);
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
- assert_se(r >= 0);
-
- r = sd_bus_message_read(reply, "s", &s);
- assert_se(r >= 0);
- fputs(s, stdout);
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
- assert_se(r >= 0);
-
- r = sd_bus_message_read(reply, "s", &s);
- assert_se(r >= 0);
- fputs(s, stdout);
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "");
- assert_se(r >= 0);
-
- bus_message_dump(reply, stdout, true);
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "org.freedesktop.systemd.ValueTest2");
- assert_se(r < 0);
- assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_INTERFACE));
- sd_bus_error_free(&error);
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
- assert_se(r < 0);
- assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD));
- sd_bus_error_free(&error);
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
- assert_se(r >= 0);
-
- bus_message_dump(reply, stdout, true);
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest", &error, NULL, "");
- assert_se(r >= 0);
-
- r = sd_bus_process(bus, &reply);
- assert_se(r > 0);
-
- assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
- bus_message_dump(reply, stdout, true);
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest2", &error, NULL, "");
- assert_se(r >= 0);
-
- r = sd_bus_process(bus, &reply);
- assert_se(r > 0);
-
- assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
- bus_message_dump(reply, stdout, true);
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesAdded", &error, NULL, "");
- assert_se(r >= 0);
-
- r = sd_bus_process(bus, &reply);
- assert_se(r > 0);
-
- assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
- bus_message_dump(reply, stdout, true);
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesRemoved", &error, NULL, "");
- assert_se(r >= 0);
-
- r = sd_bus_process(bus, &reply);
- assert_se(r > 0);
-
- assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
- bus_message_dump(reply, stdout, true);
-
- sd_bus_message_unref(reply);
- reply = NULL;
-
- r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, "");
- assert_se(r >= 0);
-
- sd_bus_flush(bus);
-
- return 0;
-}
-
-int main(int argc, char *argv[]) {
- struct context c = {};
- pthread_t s;
- void *p;
- int r, q;
-
- zero(c);
-
- c.automatic_integer_property = 4711;
- assert_se(c.automatic_string_property = strdup("dudeldu"));
-
- assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0);
-
- r = pthread_create(&s, NULL, server, &c);
- if (r != 0)
- return -r;
-
- r = client(&c);
-
- q = pthread_join(s, &p);
- if (q != 0)
- return -q;
-
- if (r < 0)
- return r;
-
- if (PTR_TO_INT(p) < 0)
- return PTR_TO_INT(p);
-
- free(c.something);
- free(c.automatic_string_property);
-
- return EXIT_SUCCESS;
-}
diff --git a/src/libsystemd/test-bus-server.c b/src/libsystemd/test-bus-server.c
deleted file mode 100644
index 0e65ee7..0000000
--- a/src/libsystemd/test-bus-server.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "log.h"
-#include "util.h"
-#include "macro.h"
-
-#include "sd-bus.h"
-#include "bus-internal.h"
-#include "bus-message.h"
-#include "bus-util.h"
-
-struct context {
- int fds[2];
-
- bool client_negotiate_unix_fds;
- bool server_negotiate_unix_fds;
-
- bool client_anonymous_auth;
- bool server_anonymous_auth;
-};
-
-static void *server(void *p) {
- struct context *c = p;
- sd_bus *bus = NULL;
- sd_id128_t id;
- bool quit = false;
- int r;
-
- assert_se(sd_id128_randomize(&id) >= 0);
-
- assert_se(sd_bus_new(&bus) >= 0);
- assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0);
- assert_se(sd_bus_set_server(bus, 1, id) >= 0);
- assert_se(sd_bus_set_anonymous(bus, c->server_anonymous_auth) >= 0);
- assert_se(sd_bus_negotiate_fds(bus, c->server_negotiate_unix_fds) >= 0);
- assert_se(sd_bus_start(bus) >= 0);
-
- while (!quit) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
-
- r = sd_bus_process(bus, &m);
- if (r < 0) {
- log_error("Failed to process requests: %s", strerror(-r));
- goto fail;
- }
-
- if (r == 0) {
- r = sd_bus_wait(bus, (uint64_t) -1);
- if (r < 0) {
- log_error("Failed to wait: %s", strerror(-r));
- goto fail;
- }
-
- continue;
- }
-
- if (!m)
- continue;
-
- log_info("Got message! member=%s", strna(sd_bus_message_get_member(m)));
-
- if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Exit")) {
-
- assert_se((sd_bus_can_send(bus, 'h') >= 1) == (c->server_negotiate_unix_fds && c->client_negotiate_unix_fds));
-
- r = sd_bus_message_new_method_return(m, &reply);
- if (r < 0) {
- log_error("Failed to allocate return: %s", strerror(-r));
- goto fail;
- }
-
- quit = true;
-
- } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
- r = sd_bus_message_new_method_error(
- m,
- &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method."),
- &reply);
- if (r < 0) {
- log_error("Failed to allocate return: %s", strerror(-r));
- goto fail;
- }
- }
-
- if (reply) {
- r = sd_bus_send(bus, reply, NULL);
- if (r < 0) {
- log_error("Failed to send reply: %s", strerror(-r));
- goto fail;
- }
- }
- }
-
- r = 0;
-
-fail:
- if (bus) {
- sd_bus_flush(bus);
- sd_bus_unref(bus);
- }
-
- return INT_TO_PTR(r);
-}
-
-static int client(struct context *c) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
- _cleanup_bus_unref_ sd_bus *bus = NULL;
- sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
-
- assert_se(sd_bus_new(&bus) >= 0);
- assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0);
- assert_se(sd_bus_negotiate_fds(bus, c->client_negotiate_unix_fds) >= 0);
- assert_se(sd_bus_set_anonymous(bus, c->client_anonymous_auth) >= 0);
- assert_se(sd_bus_start(bus) >= 0);
-
- r = sd_bus_message_new_method_call(
- bus,
- "org.freedesktop.systemd.test",
- "/",
- "org.freedesktop.systemd.test",
- "Exit",
- &m);
- if (r < 0) {
- log_error("Failed to allocate method call: %s", strerror(-r));
- return r;
- }
-
- r = sd_bus_call(bus, m, 0, &error, &reply);
- if (r < 0) {
- log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
- return r;
- }
-
- return 0;
-}
-
-static int test_one(bool client_negotiate_unix_fds, bool server_negotiate_unix_fds,
- bool client_anonymous_auth, bool server_anonymous_auth) {
-
- struct context c;
- pthread_t s;
- void *p;
- int r, q;
-
- zero(c);
-
- assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0);
-
- c.client_negotiate_unix_fds = client_negotiate_unix_fds;
- c.server_negotiate_unix_fds = server_negotiate_unix_fds;
- c.client_anonymous_auth = client_anonymous_auth;
- c.server_anonymous_auth = server_anonymous_auth;
-
- r = pthread_create(&s, NULL, server, &c);
- if (r != 0)
- return -r;
-
- r = client(&c);
-
- q = pthread_join(s, &p);
- if (q != 0)
- return -q;
-
- if (r < 0)
- return r;
-
- if (PTR_TO_INT(p) < 0)
- return PTR_TO_INT(p);
-
- return 0;
-}
-
-int main(int argc, char *argv[]) {
- int r;
-
- r = test_one(true, true, false, false);
- assert_se(r >= 0);
-
- r = test_one(true, false, false, false);
- assert_se(r >= 0);
-
- r = test_one(false, true, false, false);
- assert_se(r >= 0);
-
- r = test_one(false, false, false, false);
- assert_se(r >= 0);
-
- r = test_one(true, true, true, true);
- assert_se(r >= 0);
-
- r = test_one(true, true, false, true);
- assert_se(r >= 0);
-
- r = test_one(true, true, true, false);
- assert_se(r == -EPERM);
-
- return EXIT_SUCCESS;
-}
diff --git a/src/libsystemd/test-bus-signature.c b/src/libsystemd/test-bus-signature.c
deleted file mode 100644
index 3fc565c..0000000
--- a/src/libsystemd/test-bus-signature.c
+++ /dev/null
@@ -1,163 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include "log.h"
-#include "bus-signature.h"
-#include "bus-internal.h"
-
-int main(int argc, char *argv[]) {
- char prefix[256];
- int r;
-
- assert_se(signature_is_single("y", false));
- assert_se(signature_is_single("u", false));
- assert_se(signature_is_single("v", false));
- assert_se(signature_is_single("as", false));
- assert_se(signature_is_single("(ss)", false));
- assert_se(signature_is_single("()", false));
- assert_se(signature_is_single("(()()()()())", false));
- assert_se(signature_is_single("(((())))", false));
- assert_se(signature_is_single("((((s))))", false));
- assert_se(signature_is_single("{ss}", true));
- assert_se(signature_is_single("a{ss}", false));
- assert_se(!signature_is_single("uu", false));
- assert_se(!signature_is_single("", false));
- assert_se(!signature_is_single("(", false));
- assert_se(!signature_is_single(")", false));
- assert_se(!signature_is_single("())", false));
- assert_se(!signature_is_single("((())", false));
- assert_se(!signature_is_single("{)", false));
- assert_se(!signature_is_single("{}", true));
- assert_se(!signature_is_single("{sss}", true));
- assert_se(!signature_is_single("{s}", true));
- assert_se(!signature_is_single("{ss}", false));
- assert_se(!signature_is_single("{ass}", true));
- assert_se(!signature_is_single("a}", true));
-
- assert_se(signature_is_pair("yy"));
- assert_se(signature_is_pair("ss"));
- assert_se(signature_is_pair("sas"));
- assert_se(signature_is_pair("sv"));
- assert_se(signature_is_pair("sa(vs)"));
- assert_se(!signature_is_pair(""));
- assert_se(!signature_is_pair("va"));
- assert_se(!signature_is_pair("sss"));
- assert_se(!signature_is_pair("{s}ss"));
-
- assert_se(signature_is_valid("ssa{ss}sssub", true));
- assert_se(signature_is_valid("ssa{ss}sssub", false));
- assert_se(signature_is_valid("{ss}", true));
- assert_se(!signature_is_valid("{ss}", false));
- assert_se(signature_is_valid("", true));
- assert_se(signature_is_valid("", false));
-
- assert_se(signature_is_valid("sssusa(uuubbba(uu)uuuu)a{u(uuuvas)}", false));
-
- assert_se(!signature_is_valid("a", false));
- assert_se(signature_is_valid("as", false));
- assert_se(signature_is_valid("aas", false));
- assert_se(signature_is_valid("aaas", false));
- assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad", false));
- assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaas", false));
- assert_se(!signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaau", false));
-
- assert_se(signature_is_valid("(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))", false));
- assert_se(!signature_is_valid("((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))", false));
-
- assert_se(namespace_complex_pattern("", ""));
- assert_se(namespace_complex_pattern("foobar", "foobar"));
- assert_se(namespace_complex_pattern("foobar.waldo", "foobar.waldo"));
- assert_se(namespace_complex_pattern("foobar.", "foobar.waldo"));
- assert_se(namespace_complex_pattern("foobar.waldo", "foobar."));
- assert_se(!namespace_complex_pattern("foobar.waldo", "foobar"));
- assert_se(!namespace_complex_pattern("foobar", "foobar.waldo"));
- assert_se(!namespace_complex_pattern("", "foo"));
- assert_se(!namespace_complex_pattern("foo", ""));
- assert_se(!namespace_complex_pattern("foo.", ""));
-
- assert_se(path_complex_pattern("", ""));
- assert_se(path_complex_pattern("", "/"));
- assert_se(path_complex_pattern("/", ""));
- assert_se(path_complex_pattern("/", "/"));
- assert_se(path_complex_pattern("/foobar/", "/"));
- assert_se(path_complex_pattern("/foobar/", "/foobar"));
- assert_se(path_complex_pattern("/foobar", "/foobar"));
- assert_se(path_complex_pattern("/foobar", "/foobar/"));
- assert_se(!path_complex_pattern("/foobar", "/foobar/waldo"));
- assert_se(path_complex_pattern("/foobar/", "/foobar/waldo"));
-
- assert_se(namespace_simple_pattern("", ""));
- assert_se(namespace_simple_pattern("foobar", "foobar"));
- assert_se(namespace_simple_pattern("foobar.waldo", "foobar.waldo"));
- assert_se(namespace_simple_pattern("foobar", "foobar.waldo"));
- assert_se(!namespace_simple_pattern("foobar.waldo", "foobar"));
- assert_se(!namespace_simple_pattern("", "foo"));
- assert_se(!namespace_simple_pattern("foo", ""));
-
- assert_se(streq(object_path_startswith("/foo/bar", "/foo"), "bar"));
- assert_se(streq(object_path_startswith("/foo", "/foo"), ""));
- assert_se(streq(object_path_startswith("/foo", "/"), "foo"));
- assert_se(streq(object_path_startswith("/", "/"), ""));
- assert_se(!object_path_startswith("/foo", "/bar"));
- assert_se(!object_path_startswith("/", "/bar"));
- assert_se(!object_path_startswith("/foo", ""));
-
- assert_se(object_path_is_valid("/foo/bar"));
- assert_se(object_path_is_valid("/foo"));
- assert_se(object_path_is_valid("/"));
- assert_se(object_path_is_valid("/foo5"));
- assert_se(object_path_is_valid("/foo_5"));
- assert_se(!object_path_is_valid(""));
- assert_se(!object_path_is_valid("/foo/"));
- assert_se(!object_path_is_valid("//"));
- assert_se(!object_path_is_valid("//foo"));
- assert_se(!object_path_is_valid("/foo//bar"));
- assert_se(!object_path_is_valid("/foo/aaaäöä"));
-
- OBJECT_PATH_FOREACH_PREFIX(prefix, "/") {
- log_info("<%s>", prefix);
- assert_not_reached("???");
- }
-
- r = 0;
- OBJECT_PATH_FOREACH_PREFIX(prefix, "/xxx") {
- log_info("<%s>", prefix);
- assert_se(streq(prefix, "/"));
- assert_se(r == 0);
- r++;
- }
- assert_se(r == 1);
-
- r = 0;
- OBJECT_PATH_FOREACH_PREFIX(prefix, "/xxx/yyy/zzz") {
- log_info("<%s>", prefix);
- assert_se(r != 0 || streq(prefix, "/xxx/yyy"));
- assert_se(r != 1 || streq(prefix, "/xxx"));
- assert_se(r != 2 || streq(prefix, "/"));
- r++;
- }
- assert_se(r == 3);
-
- return 0;
-}
diff --git a/src/libsystemd/test-bus-zero-copy.c b/src/libsystemd/test-bus-zero-copy.c
deleted file mode 100644
index 5ad8ed5..0000000
--- a/src/libsystemd/test-bus-zero-copy.c
+++ /dev/null
@@ -1,197 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <fcntl.h>
-#include <sys/mman.h>
-
-#include "util.h"
-#include "log.h"
-
-#include "sd-bus.h"
-#include "sd-memfd.h"
-#include "bus-message.h"
-#include "bus-error.h"
-#include "bus-kernel.h"
-#include "bus-dump.h"
-
-#define FIRST_ARRAY 17
-#define SECOND_ARRAY 33
-
-#define STRING_SIZE 123
-
-int main(int argc, char *argv[]) {
- _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL;
- uint8_t *p;
- sd_bus *a, *b;
- int r, bus_ref;
- sd_bus_message *m;
- sd_memfd *f;
- uint64_t sz;
- uint32_t u32;
- size_t i, l;
- char *s;
-
- log_set_max_level(LOG_DEBUG);
-
- assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
-
- bus_ref = bus_kernel_create_bus(name, false, &bus_name);
- if (bus_ref == -ENOENT)
- return EXIT_TEST_SKIP;
-
- assert_se(bus_ref >= 0);
-
- address = strappend("kernel:path=", bus_name);
- assert_se(address);
-
- r = sd_bus_new(&a);
- assert_se(r >= 0);
-
- r = sd_bus_new(&b);
- assert_se(r >= 0);
-
- r = sd_bus_set_address(a, address);
- assert_se(r >= 0);
-
- r = sd_bus_set_address(b, address);
- assert_se(r >= 0);
-
- r = sd_bus_start(a);
- assert_se(r >= 0);
-
- r = sd_bus_start(b);
- assert_se(r >= 0);
-
- r = sd_bus_message_new_method_call(b, ":1.1", "/a/path", "an.inter.face", "AMethod", &m);
- assert_se(r >= 0);
-
- r = sd_bus_message_open_container(m, 'r', "aysay");
- assert_se(r >= 0);
-
- r = sd_bus_message_append_array_space(m, 'y', FIRST_ARRAY, (void**) &p);
- assert_se(r >= 0);
-
- p[0] = '<';
- memset(p+1, 'L', FIRST_ARRAY-2);
- p[FIRST_ARRAY-1] = '>';
-
- r = sd_memfd_new_and_map(&f, STRING_SIZE, (void**) &s);
- assert_se(r >= 0);
-
- s[0] = '<';
- for (i = 1; i < STRING_SIZE-2; i++)
- s[i] = '0' + (i % 10);
- s[STRING_SIZE-2] = '>';
- s[STRING_SIZE-1] = 0;
- munmap(s, STRING_SIZE);
-
- r = sd_memfd_get_size(f, &sz);
- assert_se(r >= 0);
- assert_se(sz == STRING_SIZE);
-
- r = sd_bus_message_append_string_memfd(m, f);
- assert_se(r >= 0);
-
- sd_memfd_free(f);
-
- r = sd_memfd_new_and_map(&f, SECOND_ARRAY, (void**) &p);
- assert_se(r >= 0);
-
- p[0] = '<';
- memset(p+1, 'P', SECOND_ARRAY-2);
- p[SECOND_ARRAY-1] = '>';
- munmap(p, SECOND_ARRAY);
-
- r = sd_memfd_get_size(f, &sz);
- assert_se(r >= 0);
- assert_se(sz == SECOND_ARRAY);
-
- r = sd_bus_message_append_array_memfd(m, 'y', f);
- assert_se(r >= 0);
-
- sd_memfd_free(f);
-
- r = sd_bus_message_close_container(m);
- assert_se(r >= 0);
-
- r = sd_bus_message_append(m, "u", 4711);
- assert_se(r >= 0);
-
- r = bus_message_seal(m, 55, 99*USEC_PER_SEC);
- assert_se(r >= 0);
-
- bus_message_dump(m, stdout, true);
-
- r = sd_bus_send(b, m, NULL);
- assert_se(r >= 0);
-
- sd_bus_message_unref(m);
-
- r = sd_bus_process(a, &m);
- assert_se(r > 0);
-
- bus_message_dump(m, stdout, true);
- sd_bus_message_rewind(m, true);
-
- r = sd_bus_message_enter_container(m, 'r', "aysay");
- assert_se(r > 0);
-
- r = sd_bus_message_read_array(m, 'y', (const void**) &p, &l);
- assert_se(r > 0);
- assert_se(l == FIRST_ARRAY);
-
- assert_se(p[0] == '<');
- for (i = 1; i < l-1; i++)
- assert_se(p[i] == 'L');
- assert_se(p[l-1] == '>');
-
- r = sd_bus_message_read(m, "s", &s);
- assert_se(r > 0);
-
- assert_se(s[0] == '<');
- for (i = 1; i < STRING_SIZE-2; i++)
- assert_se(s[i] == (char) ('0' + (i % 10)));
- assert_se(s[STRING_SIZE-2] == '>');
- assert_se(s[STRING_SIZE-1] == 0);
-
- r = sd_bus_message_read_array(m, 'y', (const void**) &p, &l);
- assert_se(r > 0);
- assert_se(l == SECOND_ARRAY);
-
- assert_se(p[0] == '<');
- for (i = 1; i < l-1; i++)
- assert_se(p[i] == 'P');
- assert_se(p[l-1] == '>');
-
- r = sd_bus_message_exit_container(m);
- assert_se(r > 0);
-
- r = sd_bus_message_read(m, "u", &u32);
- assert_se(r > 0);
- assert_se(u32 == 4711);
-
- sd_bus_message_unref(m);
-
- sd_bus_unref(a);
- sd_bus_unref(b);
-
- return 0;
-}
diff --git a/src/libsystemd/test-event.c b/src/libsystemd/test-event.c
deleted file mode 100644
index 28ef6a3..0000000
--- a/src/libsystemd/test-event.c
+++ /dev/null
@@ -1,248 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "sd-event.h"
-#include "log.h"
-#include "util.h"
-
-static int prepare_handler(sd_event_source *s, void *userdata) {
- log_info("preparing %c", PTR_TO_INT(userdata));
- return 1;
-}
-
-static bool got_a, got_b, got_c, got_unref;
-static unsigned got_d;
-
-static int unref_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- sd_event_source_unref(s);
- got_unref = true;
- return 0;
-}
-
-static int io_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-
- log_info("got IO on %c", PTR_TO_INT(userdata));
-
- if (userdata == INT_TO_PTR('a')) {
- assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
- assert_se(!got_a);
- got_a = true;
- } else if (userdata == INT_TO_PTR('b')) {
- assert_se(!got_b);
- got_b = true;
- } else if (userdata == INT_TO_PTR('d')) {
- got_d++;
- if (got_d < 2)
- assert_se(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT) >= 0);
- else
- assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
- } else
- assert_not_reached("Yuck!");
-
- return 1;
-}
-
-static int child_handler(sd_event_source *s, const siginfo_t *si, void *userdata) {
-
- assert(s);
- assert(si);
-
- log_info("got child on %c", PTR_TO_INT(userdata));
-
- assert(userdata == INT_TO_PTR('f'));
-
- assert_se(sd_event_exit(sd_event_source_get_event(s), 0) >= 0);
- sd_event_source_unref(s);
-
- return 1;
-}
-
-static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- sd_event_source *p;
- sigset_t ss;
- pid_t pid;
-
- assert(s);
- assert(si);
-
- log_info("got signal on %c", PTR_TO_INT(userdata));
-
- assert(userdata == INT_TO_PTR('e'));
-
- assert_se(sigemptyset(&ss) >= 0);
- assert_se(sigaddset(&ss, SIGCHLD) >= 0);
- assert_se(sigprocmask(SIG_BLOCK, &ss, NULL) >= 0);
-
- pid = fork();
- assert_se(pid >= 0);
-
- if (pid == 0)
- _exit(0);
-
- assert_se(sd_event_add_child(sd_event_source_get_event(s), pid, WEXITED, child_handler, INT_TO_PTR('f'), &p) >= 0);
- assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
-
- sd_event_source_unref(s);
-
- return 1;
-}
-
-static int defer_handler(sd_event_source *s, void *userdata) {
- sd_event_source *p;
- sigset_t ss;
-
- assert(s);
-
- log_info("got defer on %c", PTR_TO_INT(userdata));
-
- assert(userdata == INT_TO_PTR('d'));
-
- assert_se(sigemptyset(&ss) >= 0);
- assert_se(sigaddset(&ss, SIGUSR1) >= 0);
- assert_se(sigprocmask(SIG_BLOCK, &ss, NULL) >= 0);
- assert_se(sd_event_add_signal(sd_event_source_get_event(s), SIGUSR1, signal_handler, INT_TO_PTR('e'), &p) >= 0);
- assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
- raise(SIGUSR1);
-
- sd_event_source_unref(s);
-
- return 1;
-}
-
-static bool do_quit = false;
-
-static int time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
- log_info("got timer on %c", PTR_TO_INT(userdata));
-
- if (userdata == INT_TO_PTR('c')) {
-
- if (do_quit) {
- sd_event_source *p;
-
- assert_se(sd_event_add_defer(sd_event_source_get_event(s), defer_handler, INT_TO_PTR('d'), &p) >= 0);
- assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
- } else {
- assert(!got_c);
- got_c = true;
- }
- } else
- assert_not_reached("Huh?");
-
- return 2;
-}
-
-static bool got_exit = false;
-
-static int exit_handler(sd_event_source *s, void *userdata) {
- log_info("got quit handler on %c", PTR_TO_INT(userdata));
-
- got_exit = true;
-
- return 3;
-}
-
-int main(int argc, char *argv[]) {
- sd_event *e = NULL;
- sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL;
- static const char ch = 'x';
- int a[2] = { -1, -1 }, b[2] = { -1, -1}, d[2] = { -1, -1}, k[2] = { -1, -1 };
-
- assert_se(pipe(a) >= 0);
- assert_se(pipe(b) >= 0);
- assert_se(pipe(d) >= 0);
- assert_se(pipe(k) >= 0);
-
- assert_se(sd_event_default(&e) >= 0);
-
- assert_se(sd_event_set_watchdog(e, true) >= 0);
-
- /* Test whether we cleanly can destroy an io event source from its own handler */
- got_unref = false;
- assert_se(sd_event_add_io(e, k[0], EPOLLIN, unref_handler, NULL, &t) >= 0);
- assert_se(write(k[1], &ch, 1) == 1);
- assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
- assert_se(got_unref);
-
- got_a = false, got_b = false, got_c = false, got_d = 0;
-
- /* Add a oneshot handler, trigger it, re-enable it, and trigger
- * it again. */
- assert_se(sd_event_add_io(e, d[0], EPOLLIN, io_handler, INT_TO_PTR('d'), &w) >= 0);
- assert_se(sd_event_source_set_enabled(w, SD_EVENT_ONESHOT) >= 0);
- assert_se(write(d[1], &ch, 1) >= 0);
- assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
- assert_se(got_d == 1);
- assert_se(write(d[1], &ch, 1) >= 0);
- assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
- assert_se(got_d == 2);
-
- assert_se(sd_event_add_io(e, a[0], EPOLLIN, io_handler, INT_TO_PTR('a'), &x) >= 0);
- assert_se(sd_event_add_io(e, b[0], EPOLLIN, io_handler, INT_TO_PTR('b'), &y) >= 0);
- assert_se(sd_event_add_monotonic(e, 0, 0, time_handler, INT_TO_PTR('c'), &z) >= 0);
- assert_se(sd_event_add_exit(e, exit_handler, INT_TO_PTR('g'), &q) >= 0);
-
- assert_se(sd_event_source_set_priority(x, 99) >= 0);
- assert_se(sd_event_source_set_enabled(y, SD_EVENT_ONESHOT) >= 0);
- assert_se(sd_event_source_set_prepare(x, prepare_handler) >= 0);
- assert_se(sd_event_source_set_priority(z, 50) >= 0);
- assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0);
- assert_se(sd_event_source_set_prepare(z, prepare_handler) >= 0);
-
- assert_se(write(a[1], &ch, 1) >= 0);
- assert_se(write(b[1], &ch, 1) >= 0);
-
- assert_se(!got_a && !got_b && !got_c);
-
- assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
-
- assert_se(!got_a && got_b && !got_c);
-
- assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
-
- assert_se(!got_a && got_b && got_c);
-
- assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
-
- assert_se(got_a && got_b && got_c);
-
- sd_event_source_unref(x);
- sd_event_source_unref(y);
-
- do_quit = true;
- assert_se(sd_event_source_set_time(z, now(CLOCK_MONOTONIC) + 200 * USEC_PER_MSEC) >= 0);
- assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0);
-
- assert_se(sd_event_loop(e) >= 0);
-
- sd_event_source_unref(z);
- sd_event_source_unref(q);
-
- sd_event_source_unref(w);
-
- sd_event_unref(e);
-
- close_pipe(a);
- close_pipe(b);
- close_pipe(d);
- close_pipe(k);
-
- return 0;
-}
diff --git a/src/libsystemd/test-resolve.c b/src/libsystemd/test-resolve.c
deleted file mode 100644
index 3903e64..0000000
--- a/src/libsystemd/test-resolve.c
+++ /dev/null
@@ -1,160 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2005-2008 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-#include <assert.h>
-#include <signal.h>
-#include <errno.h>
-
-#include "sd-resolve.h"
-#include "resolve-util.h"
-#include "macro.h"
-
-int main(int argc, char *argv[]) {
- int r = 1, ret;
- _cleanup_resolve_free_ sd_resolve *resolve = NULL;
- _cleanup_resolve_addrinfo_free_ struct addrinfo *ai = NULL;
- _cleanup_free_ unsigned char *srv = NULL;
- sd_resolve_query *q1, *q2, *q3;
- struct addrinfo hints = {};
- struct sockaddr_in sa = {};
- char host[NI_MAXHOST] = "", serv[NI_MAXSERV] = "";
-
- signal(SIGCHLD, SIG_IGN);
-
- resolve = sd_resolve_new(2);
- if (!resolve)
- log_oom();
-
- /* Make a name -> address query */
- hints.ai_family = PF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
-
- q1 = sd_resolve_getaddrinfo(resolve, argc >= 2 ? argv[1] : "www.heise.de", NULL, &hints);
- if (!q1)
- fprintf(stderr, "sd_resolve_getaddrinfo(): %s\n", strerror(errno));
-
- /* Make an address -> name query */
- sa.sin_family = AF_INET;
- sa.sin_addr.s_addr = inet_addr(argc >= 3 ? argv[2] : "193.99.144.71");
- sa.sin_port = htons(80);
-
- q2 = sd_resolve_getnameinfo(resolve, (struct sockaddr*) &sa, sizeof(sa), 0, 1, 1);
- if (!q2)
- fprintf(stderr, "sd_resolve_getnameinfo(): %s\n", strerror(errno));
-
- /* Make a res_query() call */
- q3 = sd_resolve_res_query(resolve, "_xmpp-client._tcp.gmail.com", C_IN, T_SRV);
- if (!q3)
- fprintf(stderr, "sd_resolve_res_query(): %s\n", strerror(errno));
-
- /* Wait until the three queries are completed */
- while (!sd_resolve_isdone(resolve, q1) ||
- !sd_resolve_isdone(resolve, q2) ||
- !sd_resolve_isdone(resolve, q3)) {
- if (sd_resolve_wait(resolve, 1) < 0)
- fprintf(stderr, "sd_resolve_wait(): %s\n", strerror(errno));
- }
-
- /* Interpret the result of the name -> addr query */
- ret = sd_resolve_getaddrinfo_done(resolve, q1, &ai);
- if (ret)
- fprintf(stderr, "error: %s %i\n", gai_strerror(ret), ret);
- else {
- struct addrinfo *i;
-
- for (i = ai; i; i = i->ai_next) {
- char t[256];
- const char *p = NULL;
-
- if (i->ai_family == PF_INET)
- p = inet_ntop(AF_INET, &((struct sockaddr_in*) i->ai_addr)->sin_addr, t, sizeof(t));
- else if (i->ai_family == PF_INET6)
- p = inet_ntop(AF_INET6, &((struct sockaddr_in6*) i->ai_addr)->sin6_addr, t, sizeof(t));
-
- printf("%s\n", p);
- }
- }
-
- /* Interpret the result of the addr -> name query */
- ret = sd_resolve_getnameinfo_done(resolve, q2, host, sizeof(host), serv, sizeof(serv));
- if (ret)
- fprintf(stderr, "error: %s %i\n", gai_strerror(ret), ret);
- else
- printf("%s -- %s\n", host, serv);
-
- /* Interpret the result of the SRV lookup */
- ret = sd_resolve_res_done(resolve, q3, &srv);
- if (ret < 0) {
- fprintf(stderr, "error: %s %i\n", strerror(errno), ret);
- } else if (ret == 0) {
- fprintf(stderr, "No reply for SRV lookup\n");
- } else {
- int qdcount;
- int ancount;
- int len;
- const unsigned char *pos = srv + sizeof(HEADER);
- unsigned char *end = srv + ret;
- HEADER *head = (HEADER *)srv;
- char name[256];
-
- qdcount = ntohs(head->qdcount);
- ancount = ntohs(head->ancount);
-
- printf("%d answers for srv lookup:\n", ancount);
-
- /* Ignore the questions */
- while (qdcount-- > 0 && (len = dn_expand(srv, end, pos, name, 255)) >= 0) {
- assert(len >= 0);
- pos += len + QFIXEDSZ;
- }
-
- /* Parse the answers */
- while (ancount-- > 0 && (len = dn_expand(srv, end, pos, name, 255)) >= 0) {
- /* Ignore the initial string */
- uint16_t pref, weight, port;
- assert(len >= 0);
- pos += len;
- /* Ignore type, ttl, class and dlen */
- pos += 10;
-
- GETSHORT(pref, pos);
- GETSHORT(weight, pos);
- GETSHORT(port, pos);
- len = dn_expand(srv, end, pos, name, 255);
- printf("\tpreference: %2d weight: %2d port: %d host: %s\n",
- pref, weight, port, name);
-
- pos += len;
- }
- }
-
- r = 0;
-
- return r;
-}
diff --git a/src/libsystemd/test-rtnl.c b/src/libsystemd/test-rtnl.c
deleted file mode 100644
index 58654e9..0000000
--- a/src/libsystemd/test-rtnl.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Tom Gundersen <teg at jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <linux/rtnetlink.h>
-#include <netinet/ether.h>
-
-#include "util.h"
-#include "macro.h"
-#include "sd-rtnl.h"
-#include "socket-util.h"
-#include "rtnl-util.h"
-#include "event-util.h"
-
-static void test_link_configure(sd_rtnl *rtnl, int ifindex) {
- _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message;
- uint16_t type;
- const char *mac = "98:fe:94:3f:c6:18", *name = "test";
- unsigned int mtu = 1450;
- void *data;
-
- /* we'd really like to test NEWLINK, but let's not mess with the running kernel */
- assert(sd_rtnl_message_link_new(RTM_GETLINK, ifindex, &message) >= 0);
- assert(sd_rtnl_message_append_string(message, IFLA_IFNAME, name) >= 0);
- assert(sd_rtnl_message_append_ether_addr(message, IFLA_ADDRESS, ether_aton(mac)) >= 0);
- assert(sd_rtnl_message_append_u32(message, IFLA_MTU, mtu) >= 0);
-
- assert(sd_rtnl_message_read(message, &type, &data) > 0);
- assert(type == IFLA_IFNAME);
- assert(streq(name, (char *) data));
-
- assert(sd_rtnl_message_read(message, &type, &data) > 0);
- assert(type == IFLA_ADDRESS);
- assert(streq(mac, ether_ntoa(data)));
-
- assert(sd_rtnl_message_read(message, &type, &data) > 0);
- assert(type == IFLA_MTU);
- assert(mtu == *(unsigned int *) data);
-
- assert(sd_rtnl_call(rtnl, message, 0, NULL) == 1);
-}
-
-static void test_route(void) {
- _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req;
- struct in_addr addr;
- uint32_t index = 2;
- uint16_t type;
- void *data;
- int r;
-
- r = sd_rtnl_message_route_new(RTM_NEWROUTE, AF_INET, &req);
- if (r < 0) {
- log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r));
- return;
- }
-
- addr.s_addr = htonl(INADDR_LOOPBACK);
-
- r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &addr);
- if (r < 0) {
- log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r));
- return;
- }
-
- r = sd_rtnl_message_append_u32(req, RTA_OIF, index);
- if (r < 0) {
- log_error("Could not append RTA_OIF attribute: %s", strerror(-r));
- return;
- }
-
- assert(sd_rtnl_message_read(req, &type, &data) > 0);
- assert(type == RTA_GATEWAY);
- assert(((struct in_addr *)data)->s_addr == addr.s_addr);
-
- assert(sd_rtnl_message_read(req, &type, &data) > 0);
- assert(type == RTA_OIF);
- assert(*(uint32_t *) data == index);
-}
-
-static void test_multiple(void) {
- sd_rtnl *rtnl1, *rtnl2;
-
- assert(sd_rtnl_open(0, &rtnl1) >= 0);
- assert(sd_rtnl_open(0, &rtnl2) >= 0);
-
- rtnl1 = sd_rtnl_unref(rtnl1);
- rtnl2 = sd_rtnl_unref(rtnl2);
-}
-
-static int link_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
- void *data;
- uint16_t type;
- char *ifname = userdata;
-
- assert(rtnl);
- assert(m);
-
- log_info("got link info about %s", ifname);
- free(ifname);
-
- while (sd_rtnl_message_read(m, &type, &data) > 0) {
- switch (type) {
-// case IFLA_MTU:
-// assert(*(unsigned int *) data == 65536);
-// break;
-// case IFLA_QDISC:
-// assert(streq((char *) data, "noqueue"));
-// break;
- case IFLA_IFNAME:
- assert(streq((char *) data, "lo"));
- break;
- }
- }
-
- return 1;
-}
-
-static void test_event_loop(int ifindex) {
- _cleanup_event_unref_ sd_event *event = NULL;
- _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
- _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
- char *ifname;
-
- ifname = strdup("lo2");
- assert(ifname);
-
- assert(sd_rtnl_open(0, &rtnl) >= 0);
- assert(sd_rtnl_message_link_new(RTM_GETLINK, ifindex, &m) >= 0);
-
- assert(sd_rtnl_call_async(rtnl, m, &link_handler, ifname, 0, NULL) >= 0);
-
- assert(sd_event_default(&event) >= 0);
-
- assert(sd_rtnl_attach_event(rtnl, event, 0) >= 0);
-
- assert(sd_event_run(event, 0) >= 0);
-
- assert(sd_rtnl_detach_event(rtnl) >= 0);
-}
-
-static int pipe_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
- int *counter = userdata;
-
- (*counter) --;
-
- log_info("got reply, %d left in pipe", *counter);
-
- return sd_rtnl_message_get_errno(m);
-}
-
-static void test_async(int ifindex) {
- _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
- _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL, *r = NULL;
- uint32_t serial;
- char *ifname;
-
- ifname = strdup("lo");
- assert(ifname);
-
- assert(sd_rtnl_open(0, &rtnl) >= 0);
-
- assert(sd_rtnl_message_link_new(RTM_GETLINK, ifindex, &m) >= 0);
-
- assert(sd_rtnl_call_async(rtnl, m, &link_handler, ifname, 0, &serial) >= 0);
-
- assert(sd_rtnl_wait(rtnl, 0) >= 0);
- assert(sd_rtnl_process(rtnl, &r) >= 0);
-}
-
-static void test_pipe(int ifindex) {
- _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
- _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m1 = NULL, *m2 = NULL;
- int counter = 0;
-
- assert(sd_rtnl_open(0, &rtnl) >= 0);
-
- assert(sd_rtnl_message_link_new(RTM_GETLINK, ifindex, &m1) >= 0);
- assert(sd_rtnl_message_link_new(RTM_GETLINK, ifindex, &m2) >= 0);
-
- counter ++;
- assert(sd_rtnl_call_async(rtnl, m1, &pipe_handler, &counter, 0, NULL) >= 0);
-
- counter ++;
- assert(sd_rtnl_call_async(rtnl, m2, &pipe_handler, &counter, 0, NULL) >= 0);
-
- while (counter > 0) {
- assert(sd_rtnl_wait(rtnl, 0) >= 0);
- assert(sd_rtnl_process(rtnl, NULL) >= 0);
- }
-}
-
-static void test_container(void) {
- _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
- uint16_t type;
- void *data;
-
- assert(sd_rtnl_message_link_new(RTM_NEWLINK, 0, &m) >= 0);
-
- assert(sd_rtnl_message_open_container(m, IFLA_LINKINFO) >= 0);
- assert(sd_rtnl_message_open_container(m, IFLA_LINKINFO) == -EINVAL);
- assert(sd_rtnl_message_append_string(m, IFLA_INFO_KIND, "kind") >= 0);
- assert(sd_rtnl_message_close_container(m) >= 0);
- assert(sd_rtnl_message_close_container(m) == -EINVAL);
-
- assert(sd_rtnl_message_read(m, &type, &data) == -EINVAL);
-
-/* TODO: add support for entering containers
- assert(sd_rtnl_message_read(m, &type, &data) > 0);
- assert(type == IFLA_INFO_KIND);
- assert(streq("kind", (char *) data));
-
- assert(sd_rtnl_message_read(m, &type, &data) == 0);
-*/
-}
-
-static void test_match(void) {
- _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
-
- assert(sd_rtnl_open(0, &rtnl) >= 0);
-
- assert(sd_rtnl_add_match(rtnl, RTM_NEWLINK, &link_handler, NULL) >= 0);
- assert(sd_rtnl_add_match(rtnl, RTM_NEWLINK, &link_handler, NULL) >= 0);
-
- assert(sd_rtnl_remove_match(rtnl, RTM_NEWLINK, &link_handler, NULL) == 1);
- assert(sd_rtnl_remove_match(rtnl, RTM_NEWLINK, &link_handler, NULL) == 1);
- assert(sd_rtnl_remove_match(rtnl, RTM_NEWLINK, &link_handler, NULL) == 0);
-}
-
-int main(void) {
- sd_rtnl *rtnl;
- sd_rtnl_message *m;
- sd_rtnl_message *r;
- void *data;
- int if_loopback;
- uint16_t type;
- unsigned int mtu = 0;
- unsigned int *mtu_reply;
-
- test_match();
-
- test_multiple();
-
- test_route();
-
- test_container();
-
- assert(sd_rtnl_open(0, &rtnl) >= 0);
- assert(rtnl);
-
- if_loopback = (int) if_nametoindex("lo");
- assert(if_loopback > 0);
-
- test_async(if_loopback);
-
- test_pipe(if_loopback);
-
- test_event_loop(if_loopback);
-
- test_link_configure(rtnl, if_loopback);
-
- assert(sd_rtnl_message_link_new(RTM_GETLINK, if_loopback, &m) >= 0);
- assert(m);
-
- assert(sd_rtnl_message_get_type(m, &type) >= 0);
- assert(type == RTM_GETLINK);
-
- assert(sd_rtnl_message_read(m, &type, &data) == 0);
-
- assert(sd_rtnl_call(rtnl, m, 0, &r) == 1);
- assert(sd_rtnl_message_get_type(r, &type) >= 0);
- assert(type == RTM_NEWLINK);
-
- assert(sd_rtnl_message_read(m, &type, &data) == 0);
- assert((r = sd_rtnl_message_unref(r)) == NULL);
-
- assert(sd_rtnl_call(rtnl, m, -1, &r) == -EPERM);
- assert((m = sd_rtnl_message_unref(m)) == NULL);
- assert((r = sd_rtnl_message_unref(r)) == NULL);
-
- assert(sd_rtnl_message_link_new(RTM_GETLINK, if_loopback, &m) >= 0);
- assert(m);
-
- assert(sd_rtnl_message_append_u32(m, IFLA_MTU, mtu) >= 0);
- assert(sd_rtnl_message_read(m, &type, (void **) &mtu_reply) == 1);
-
- assert(type == IFLA_MTU);
- assert(*mtu_reply == 0);
-
- assert(sd_rtnl_message_read(m, &type, &data) == 0);
-
- assert(sd_rtnl_call(rtnl, m, -1, &r) == 1);
- while (sd_rtnl_message_read(r, &type, &data) > 0) {
- switch (type) {
-// case IFLA_MTU:
-// assert(*(unsigned int *) data == 65536);
-// break;
-// case IFLA_QDISC:
-// assert(streq((char *) data, "noqueue"));
-// break;
- case IFLA_IFNAME:
- assert(streq((char *) data, "lo"));
- break;
- }
- }
-
- assert(sd_rtnl_flush(rtnl) >= 0);
-
- assert((m = sd_rtnl_message_unref(m)) == NULL);
- assert((r = sd_rtnl_message_unref(r)) == NULL);
- assert((rtnl = sd_rtnl_unref(rtnl)) == NULL);
-
- return EXIT_SUCCESS;
-}
More information about the systemd-commits
mailing list