From 38b93f33c4130a69b6ff1777ee59511f601abff6 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 27 Jul 2016 21:21:19 -0400 Subject: ./move.sh --- src/libsystemd/Makefile | 111 +- src/libsystemd/include/systemd/_sd-common.h | 83 + src/libsystemd/include/systemd/sd-bus-protocol.h | 102 + src/libsystemd/include/systemd/sd-bus-vtable.h | 141 + src/libsystemd/include/systemd/sd-bus.h | 456 ++ src/libsystemd/include/systemd/sd-daemon.h | 289 + src/libsystemd/include/systemd/sd-device.h | 101 + src/libsystemd/include/systemd/sd-dhcp-client.h | 158 + src/libsystemd/include/systemd/sd-dhcp-lease.h | 67 + src/libsystemd/include/systemd/sd-dhcp-server.h | 65 + src/libsystemd/include/systemd/sd-dhcp6-client.h | 135 + src/libsystemd/include/systemd/sd-dhcp6-lease.h | 52 + src/libsystemd/include/systemd/sd-event.h | 142 + src/libsystemd/include/systemd/sd-hwdb.h | 49 + src/libsystemd/include/systemd/sd-id128.h | 115 + src/libsystemd/include/systemd/sd-ipv4acd.h | 60 + src/libsystemd/include/systemd/sd-ipv4ll.h | 60 + src/libsystemd/include/systemd/sd-journal.h | 175 + src/libsystemd/include/systemd/sd-lldp.h | 177 + src/libsystemd/include/systemd/sd-login.h | 245 + src/libsystemd/include/systemd/sd-messages.h | 91 + src/libsystemd/include/systemd/sd-ndisc.h | 84 + src/libsystemd/include/systemd/sd-netlink.h | 163 + src/libsystemd/include/systemd/sd-network.h | 176 + src/libsystemd/include/systemd/sd-path.h | 91 + src/libsystemd/include/systemd/sd-resolve.h | 117 + src/libsystemd/include/systemd/sd-utf8.h | 32 + src/libsystemd/sd-bus/DIFFERENCES | 25 - src/libsystemd/sd-bus/GVARIANT-SERIALIZATION | 110 - src/libsystemd/sd-bus/Makefile | 1 - src/libsystemd/sd-bus/PORTING-DBUS1 | 535 -- src/libsystemd/sd-bus/bus-bloom.c | 156 - src/libsystemd/sd-bus/bus-bloom.h | 43 - src/libsystemd/sd-bus/bus-common-errors.c | 87 - src/libsystemd/sd-bus/bus-common-errors.h | 86 - src/libsystemd/sd-bus/bus-container.c | 277 - src/libsystemd/sd-bus/bus-container.h | 25 - src/libsystemd/sd-bus/bus-control.c | 1588 ------ src/libsystemd/sd-bus/bus-control.h | 32 - src/libsystemd/sd-bus/bus-convenience.c | 626 --- src/libsystemd/sd-bus/bus-creds.c | 1349 ----- src/libsystemd/sd-bus/bus-creds.h | 90 - src/libsystemd/sd-bus/bus-dump.c | 602 -- src/libsystemd/sd-bus/bus-dump.h | 37 - src/libsystemd/sd-bus/bus-error.c | 608 -- src/libsystemd/sd-bus/bus-error.h | 64 - src/libsystemd/sd-bus/bus-gvariant.c | 311 - src/libsystemd/sd-bus/bus-gvariant.h | 30 - src/libsystemd/sd-bus/bus-internal.c | 374 -- src/libsystemd/sd-bus/bus-internal.h | 399 -- src/libsystemd/sd-bus/bus-introspect.c | 212 - src/libsystemd/sd-bus/bus-introspect.h | 40 - src/libsystemd/sd-bus/bus-kernel.c | 1782 ------ src/libsystemd/sd-bus/bus-kernel.h | 93 - src/libsystemd/sd-bus/bus-match.c | 1218 ---- src/libsystemd/sd-bus/bus-match.h | 100 - src/libsystemd/sd-bus/bus-message.c | 5939 -------------------- src/libsystemd/sd-bus/bus-message.h | 244 - src/libsystemd/sd-bus/bus-objects.c | 2806 --------- src/libsystemd/sd-bus/bus-objects.h | 25 - src/libsystemd/sd-bus/bus-protocol.h | 180 - src/libsystemd/sd-bus/bus-signature.c | 158 - src/libsystemd/sd-bus/bus-signature.h | 28 - src/libsystemd/sd-bus/bus-slot.c | 286 - src/libsystemd/sd-bus/bus-slot.h | 28 - src/libsystemd/sd-bus/bus-socket.c | 1064 ---- src/libsystemd/sd-bus/bus-socket.h | 37 - src/libsystemd/sd-bus/bus-track.c | 337 -- src/libsystemd/sd-bus/bus-track.h | 22 - src/libsystemd/sd-bus/bus-type.c | 176 - src/libsystemd/sd-bus/bus-type.h | 37 - src/libsystemd/sd-bus/busctl-introspect.c | 790 --- src/libsystemd/sd-bus/busctl-introspect.h | 32 - src/libsystemd/sd-bus/busctl.c | 2086 ------- src/libsystemd/sd-bus/kdbus.h | 980 ---- src/libsystemd/sd-bus/sd-bus.c | 3791 ------------- src/libsystemd/sd-bus/test-bus-benchmark.c | 371 -- src/libsystemd/sd-bus/test-bus-chat.c | 560 -- src/libsystemd/sd-bus/test-bus-cleanup.c | 95 - src/libsystemd/sd-bus/test-bus-creds.c | 50 - src/libsystemd/sd-bus/test-bus-error.c | 232 - src/libsystemd/sd-bus/test-bus-gvariant.c | 224 - src/libsystemd/sd-bus/test-bus-introspect.c | 63 - src/libsystemd/sd-bus/test-bus-kernel-bloom.c | 141 - src/libsystemd/sd-bus/test-bus-kernel.c | 190 - src/libsystemd/sd-bus/test-bus-marshal.c | 432 -- src/libsystemd/sd-bus/test-bus-match.c | 159 - src/libsystemd/sd-bus/test-bus-objects.c | 555 -- src/libsystemd/sd-bus/test-bus-server.c | 216 - src/libsystemd/sd-bus/test-bus-signature.c | 164 - src/libsystemd/sd-bus/test-bus-zero-copy.c | 210 - src/libsystemd/sd-daemon/Makefile | 1 - src/libsystemd/sd-daemon/sd-daemon.c | 622 -- src/libsystemd/sd-device/Makefile | 1 - .../sd-device/device-enumerator-private.h | 34 - src/libsystemd/sd-device/device-enumerator.c | 986 ---- src/libsystemd/sd-device/device-internal.h | 126 - src/libsystemd/sd-device/device-private.c | 1119 ---- src/libsystemd/sd-device/device-private.h | 68 - src/libsystemd/sd-device/device-util.h | 52 - src/libsystemd/sd-device/sd-device.c | 1884 ------- src/libsystemd/sd-event/Makefile | 1 - src/libsystemd/sd-event/sd-event.c | 2898 ---------- src/libsystemd/sd-event/test-event.c | 361 -- src/libsystemd/sd-hwdb/Makefile | 1 - src/libsystemd/sd-hwdb/hwdb-internal.h | 72 - src/libsystemd/sd-hwdb/hwdb-util.h | 26 - src/libsystemd/sd-hwdb/sd-hwdb.c | 470 -- src/libsystemd/sd-id128/Makefile | 1 - src/libsystemd/sd-id128/sd-id128.c | 225 - src/libsystemd/sd-login/Makefile | 1 - src/libsystemd/sd-login/sd-login.c | 1062 ---- src/libsystemd/sd-login/test-login.c | 264 - src/libsystemd/sd-netlink/Makefile | 1 - src/libsystemd/sd-netlink/local-addresses.c | 275 - src/libsystemd/sd-netlink/local-addresses.h | 36 - src/libsystemd/sd-netlink/netlink-internal.h | 137 - src/libsystemd/sd-netlink/netlink-message.c | 961 ---- src/libsystemd/sd-netlink/netlink-socket.c | 474 -- src/libsystemd/sd-netlink/netlink-types.c | 684 --- src/libsystemd/sd-netlink/netlink-types.h | 94 - src/libsystemd/sd-netlink/netlink-util.c | 170 - src/libsystemd/sd-netlink/netlink-util.h | 39 - src/libsystemd/sd-netlink/rtnl-message.c | 702 --- src/libsystemd/sd-netlink/sd-netlink.c | 954 ---- src/libsystemd/sd-netlink/test-local-addresses.c | 56 - src/libsystemd/sd-netlink/test-netlink.c | 440 -- src/libsystemd/sd-network/Makefile | 1 - src/libsystemd/sd-network/network-util.c | 37 - src/libsystemd/sd-network/network-util.h | 24 - src/libsystemd/sd-network/sd-network.c | 400 -- src/libsystemd/sd-path/Makefile | 1 - src/libsystemd/sd-path/sd-path.c | 638 --- src/libsystemd/sd-resolve/Makefile | 1 - src/libsystemd/sd-resolve/sd-resolve.c | 1245 ---- src/libsystemd/sd-resolve/test-resolve.c | 114 - src/libsystemd/sd-utf8/Makefile | 1 - src/libsystemd/sd-utf8/sd-utf8.c | 35 - src/libsystemd/src/Makefile | 329 ++ src/libsystemd/src/sd-bus/DIFFERENCES | 25 + src/libsystemd/src/sd-bus/GVARIANT-SERIALIZATION | 110 + src/libsystemd/src/sd-bus/PORTING-DBUS1 | 535 ++ src/libsystemd/src/sd-bus/bus-bloom.c | 156 + src/libsystemd/src/sd-bus/bus-bloom.h | 43 + src/libsystemd/src/sd-bus/bus-common-errors.c | 87 + src/libsystemd/src/sd-bus/bus-common-errors.h | 86 + src/libsystemd/src/sd-bus/bus-container.c | 277 + src/libsystemd/src/sd-bus/bus-container.h | 25 + src/libsystemd/src/sd-bus/bus-control.c | 1588 ++++++ src/libsystemd/src/sd-bus/bus-control.h | 32 + src/libsystemd/src/sd-bus/bus-convenience.c | 626 +++ src/libsystemd/src/sd-bus/bus-creds.c | 1349 +++++ src/libsystemd/src/sd-bus/bus-creds.h | 90 + src/libsystemd/src/sd-bus/bus-dump.c | 602 ++ src/libsystemd/src/sd-bus/bus-dump.h | 37 + src/libsystemd/src/sd-bus/bus-error.c | 608 ++ src/libsystemd/src/sd-bus/bus-error.h | 64 + src/libsystemd/src/sd-bus/bus-gvariant.c | 311 + src/libsystemd/src/sd-bus/bus-gvariant.h | 30 + src/libsystemd/src/sd-bus/bus-internal.c | 374 ++ src/libsystemd/src/sd-bus/bus-internal.h | 399 ++ src/libsystemd/src/sd-bus/bus-introspect.c | 212 + src/libsystemd/src/sd-bus/bus-introspect.h | 40 + src/libsystemd/src/sd-bus/bus-kernel.c | 1782 ++++++ src/libsystemd/src/sd-bus/bus-kernel.h | 93 + src/libsystemd/src/sd-bus/bus-match.c | 1218 ++++ src/libsystemd/src/sd-bus/bus-match.h | 100 + src/libsystemd/src/sd-bus/bus-message.c | 5939 ++++++++++++++++++++ src/libsystemd/src/sd-bus/bus-message.h | 244 + src/libsystemd/src/sd-bus/bus-objects.c | 2806 +++++++++ src/libsystemd/src/sd-bus/bus-objects.h | 25 + src/libsystemd/src/sd-bus/bus-protocol.h | 180 + src/libsystemd/src/sd-bus/bus-signature.c | 158 + src/libsystemd/src/sd-bus/bus-signature.h | 28 + src/libsystemd/src/sd-bus/bus-slot.c | 286 + src/libsystemd/src/sd-bus/bus-slot.h | 28 + src/libsystemd/src/sd-bus/bus-socket.c | 1064 ++++ src/libsystemd/src/sd-bus/bus-socket.h | 37 + src/libsystemd/src/sd-bus/bus-track.c | 337 ++ src/libsystemd/src/sd-bus/bus-track.h | 22 + src/libsystemd/src/sd-bus/bus-type.c | 176 + src/libsystemd/src/sd-bus/bus-type.h | 37 + src/libsystemd/src/sd-bus/kdbus.h | 980 ++++ src/libsystemd/src/sd-bus/sd-bus.c | 3791 +++++++++++++ src/libsystemd/src/sd-bus/test-bus-benchmark.c | 371 ++ src/libsystemd/src/sd-bus/test-bus-chat.c | 560 ++ src/libsystemd/src/sd-bus/test-bus-cleanup.c | 95 + src/libsystemd/src/sd-bus/test-bus-creds.c | 50 + src/libsystemd/src/sd-bus/test-bus-error.c | 232 + src/libsystemd/src/sd-bus/test-bus-gvariant.c | 224 + src/libsystemd/src/sd-bus/test-bus-introspect.c | 63 + src/libsystemd/src/sd-bus/test-bus-kernel-bloom.c | 141 + src/libsystemd/src/sd-bus/test-bus-kernel.c | 190 + src/libsystemd/src/sd-bus/test-bus-marshal.c | 432 ++ src/libsystemd/src/sd-bus/test-bus-match.c | 159 + src/libsystemd/src/sd-bus/test-bus-objects.c | 555 ++ src/libsystemd/src/sd-bus/test-bus-server.c | 216 + src/libsystemd/src/sd-bus/test-bus-signature.c | 164 + src/libsystemd/src/sd-bus/test-bus-zero-copy.c | 210 + src/libsystemd/src/sd-daemon/sd-daemon.c | 622 ++ .../src/sd-device/device-enumerator-private.h | 34 + src/libsystemd/src/sd-device/device-enumerator.c | 986 ++++ src/libsystemd/src/sd-device/device-internal.h | 126 + src/libsystemd/src/sd-device/device-private.c | 1119 ++++ src/libsystemd/src/sd-device/device-private.h | 68 + src/libsystemd/src/sd-device/device-util.h | 52 + src/libsystemd/src/sd-device/sd-device.c | 1884 +++++++ src/libsystemd/src/sd-event/sd-event.c | 2898 ++++++++++ src/libsystemd/src/sd-event/test-event.c | 361 ++ src/libsystemd/src/sd-hwdb/hwdb-internal.h | 72 + src/libsystemd/src/sd-hwdb/hwdb-util.h | 26 + src/libsystemd/src/sd-hwdb/sd-hwdb.c | 470 ++ src/libsystemd/src/sd-id128/sd-id128.c | 225 + src/libsystemd/src/sd-journal/Makefile | 37 + src/libsystemd/src/sd-journal/audit-type.c | 29 + src/libsystemd/src/sd-journal/audit-type.h | 37 + src/libsystemd/src/sd-journal/catalog.c | 767 +++ src/libsystemd/src/sd-journal/catalog.h | 36 + src/libsystemd/src/sd-journal/compress.c | 683 +++ src/libsystemd/src/sd-journal/compress.h | 85 + src/libsystemd/src/sd-journal/fsprg.c | 374 ++ src/libsystemd/src/sd-journal/fsprg.h | 65 + .../src/sd-journal/journal-authenticate.c | 551 ++ .../src/sd-journal/journal-authenticate.h | 41 + src/libsystemd/src/sd-journal/journal-def.h | 237 + src/libsystemd/src/sd-journal/journal-file.c | 3616 ++++++++++++ src/libsystemd/src/sd-journal/journal-file.h | 265 + src/libsystemd/src/sd-journal/journal-internal.h | 143 + src/libsystemd/src/sd-journal/journal-send.c | 559 ++ src/libsystemd/src/sd-journal/journal-vacuum.c | 349 ++ src/libsystemd/src/sd-journal/journal-vacuum.h | 27 + src/libsystemd/src/sd-journal/journal-verify.c | 1294 +++++ src/libsystemd/src/sd-journal/journal-verify.h | 24 + src/libsystemd/src/sd-journal/lookup3.c | 1009 ++++ src/libsystemd/src/sd-journal/lookup3.h | 22 + src/libsystemd/src/sd-journal/mmap-cache.c | 725 +++ src/libsystemd/src/sd-journal/mmap-cache.h | 49 + src/libsystemd/src/sd-journal/sd-journal.c | 2985 ++++++++++ src/libsystemd/src/sd-login/sd-login.c | 1062 ++++ src/libsystemd/src/sd-login/test-login.c | 264 + src/libsystemd/src/sd-netlink/local-addresses.c | 275 + src/libsystemd/src/sd-netlink/local-addresses.h | 36 + src/libsystemd/src/sd-netlink/netlink-internal.h | 137 + src/libsystemd/src/sd-netlink/netlink-message.c | 961 ++++ src/libsystemd/src/sd-netlink/netlink-socket.c | 474 ++ src/libsystemd/src/sd-netlink/netlink-types.c | 684 +++ src/libsystemd/src/sd-netlink/netlink-types.h | 94 + src/libsystemd/src/sd-netlink/netlink-util.c | 170 + src/libsystemd/src/sd-netlink/netlink-util.h | 39 + src/libsystemd/src/sd-netlink/rtnl-message.c | 702 +++ src/libsystemd/src/sd-netlink/sd-netlink.c | 954 ++++ .../src/sd-netlink/test-local-addresses.c | 56 + src/libsystemd/src/sd-netlink/test-netlink.c | 440 ++ src/libsystemd/src/sd-network/network-util.c | 37 + src/libsystemd/src/sd-network/network-util.h | 24 + src/libsystemd/src/sd-network/sd-network.c | 400 ++ src/libsystemd/src/sd-path/sd-path.c | 638 +++ src/libsystemd/src/sd-resolve/sd-resolve.c | 1245 ++++ src/libsystemd/src/sd-resolve/test-resolve.c | 114 + src/libsystemd/src/sd-utf8/sd-utf8.c | 35 + src/libsystemd/src/subdir.mk | 29 + 261 files changed, 66386 insertions(+), 51404 deletions(-) mode change 120000 => 100644 src/libsystemd/Makefile create mode 100644 src/libsystemd/include/systemd/_sd-common.h create mode 100644 src/libsystemd/include/systemd/sd-bus-protocol.h create mode 100644 src/libsystemd/include/systemd/sd-bus-vtable.h create mode 100644 src/libsystemd/include/systemd/sd-bus.h create mode 100644 src/libsystemd/include/systemd/sd-daemon.h create mode 100644 src/libsystemd/include/systemd/sd-device.h create mode 100644 src/libsystemd/include/systemd/sd-dhcp-client.h create mode 100644 src/libsystemd/include/systemd/sd-dhcp-lease.h create mode 100644 src/libsystemd/include/systemd/sd-dhcp-server.h create mode 100644 src/libsystemd/include/systemd/sd-dhcp6-client.h create mode 100644 src/libsystemd/include/systemd/sd-dhcp6-lease.h create mode 100644 src/libsystemd/include/systemd/sd-event.h create mode 100644 src/libsystemd/include/systemd/sd-hwdb.h create mode 100644 src/libsystemd/include/systemd/sd-id128.h create mode 100644 src/libsystemd/include/systemd/sd-ipv4acd.h create mode 100644 src/libsystemd/include/systemd/sd-ipv4ll.h create mode 100644 src/libsystemd/include/systemd/sd-journal.h create mode 100644 src/libsystemd/include/systemd/sd-lldp.h create mode 100644 src/libsystemd/include/systemd/sd-login.h create mode 100644 src/libsystemd/include/systemd/sd-messages.h create mode 100644 src/libsystemd/include/systemd/sd-ndisc.h create mode 100644 src/libsystemd/include/systemd/sd-netlink.h create mode 100644 src/libsystemd/include/systemd/sd-network.h create mode 100644 src/libsystemd/include/systemd/sd-path.h create mode 100644 src/libsystemd/include/systemd/sd-resolve.h create mode 100644 src/libsystemd/include/systemd/sd-utf8.h delete mode 100644 src/libsystemd/sd-bus/DIFFERENCES delete mode 100644 src/libsystemd/sd-bus/GVARIANT-SERIALIZATION delete mode 120000 src/libsystemd/sd-bus/Makefile delete mode 100644 src/libsystemd/sd-bus/PORTING-DBUS1 delete mode 100644 src/libsystemd/sd-bus/bus-bloom.c delete mode 100644 src/libsystemd/sd-bus/bus-bloom.h delete mode 100644 src/libsystemd/sd-bus/bus-common-errors.c delete mode 100644 src/libsystemd/sd-bus/bus-common-errors.h delete mode 100644 src/libsystemd/sd-bus/bus-container.c delete mode 100644 src/libsystemd/sd-bus/bus-container.h delete mode 100644 src/libsystemd/sd-bus/bus-control.c delete mode 100644 src/libsystemd/sd-bus/bus-control.h delete mode 100644 src/libsystemd/sd-bus/bus-convenience.c delete mode 100644 src/libsystemd/sd-bus/bus-creds.c delete mode 100644 src/libsystemd/sd-bus/bus-creds.h delete mode 100644 src/libsystemd/sd-bus/bus-dump.c delete mode 100644 src/libsystemd/sd-bus/bus-dump.h delete mode 100644 src/libsystemd/sd-bus/bus-error.c delete mode 100644 src/libsystemd/sd-bus/bus-error.h delete mode 100644 src/libsystemd/sd-bus/bus-gvariant.c delete mode 100644 src/libsystemd/sd-bus/bus-gvariant.h delete mode 100644 src/libsystemd/sd-bus/bus-internal.c delete mode 100644 src/libsystemd/sd-bus/bus-internal.h delete mode 100644 src/libsystemd/sd-bus/bus-introspect.c delete mode 100644 src/libsystemd/sd-bus/bus-introspect.h delete mode 100644 src/libsystemd/sd-bus/bus-kernel.c delete mode 100644 src/libsystemd/sd-bus/bus-kernel.h delete mode 100644 src/libsystemd/sd-bus/bus-match.c delete mode 100644 src/libsystemd/sd-bus/bus-match.h delete mode 100644 src/libsystemd/sd-bus/bus-message.c delete mode 100644 src/libsystemd/sd-bus/bus-message.h delete mode 100644 src/libsystemd/sd-bus/bus-objects.c delete mode 100644 src/libsystemd/sd-bus/bus-objects.h delete mode 100644 src/libsystemd/sd-bus/bus-protocol.h delete mode 100644 src/libsystemd/sd-bus/bus-signature.c delete mode 100644 src/libsystemd/sd-bus/bus-signature.h delete mode 100644 src/libsystemd/sd-bus/bus-slot.c delete mode 100644 src/libsystemd/sd-bus/bus-slot.h delete mode 100644 src/libsystemd/sd-bus/bus-socket.c delete mode 100644 src/libsystemd/sd-bus/bus-socket.h delete mode 100644 src/libsystemd/sd-bus/bus-track.c delete mode 100644 src/libsystemd/sd-bus/bus-track.h delete mode 100644 src/libsystemd/sd-bus/bus-type.c delete mode 100644 src/libsystemd/sd-bus/bus-type.h delete mode 100644 src/libsystemd/sd-bus/busctl-introspect.c delete mode 100644 src/libsystemd/sd-bus/busctl-introspect.h delete mode 100644 src/libsystemd/sd-bus/busctl.c delete mode 100644 src/libsystemd/sd-bus/kdbus.h delete mode 100644 src/libsystemd/sd-bus/sd-bus.c delete mode 100644 src/libsystemd/sd-bus/test-bus-benchmark.c delete mode 100644 src/libsystemd/sd-bus/test-bus-chat.c delete mode 100644 src/libsystemd/sd-bus/test-bus-cleanup.c delete mode 100644 src/libsystemd/sd-bus/test-bus-creds.c delete mode 100644 src/libsystemd/sd-bus/test-bus-error.c delete mode 100644 src/libsystemd/sd-bus/test-bus-gvariant.c delete mode 100644 src/libsystemd/sd-bus/test-bus-introspect.c delete mode 100644 src/libsystemd/sd-bus/test-bus-kernel-bloom.c delete mode 100644 src/libsystemd/sd-bus/test-bus-kernel.c delete mode 100644 src/libsystemd/sd-bus/test-bus-marshal.c delete mode 100644 src/libsystemd/sd-bus/test-bus-match.c delete mode 100644 src/libsystemd/sd-bus/test-bus-objects.c delete mode 100644 src/libsystemd/sd-bus/test-bus-server.c delete mode 100644 src/libsystemd/sd-bus/test-bus-signature.c delete mode 100644 src/libsystemd/sd-bus/test-bus-zero-copy.c delete mode 120000 src/libsystemd/sd-daemon/Makefile delete mode 100644 src/libsystemd/sd-daemon/sd-daemon.c delete mode 120000 src/libsystemd/sd-device/Makefile delete mode 100644 src/libsystemd/sd-device/device-enumerator-private.h delete mode 100644 src/libsystemd/sd-device/device-enumerator.c delete mode 100644 src/libsystemd/sd-device/device-internal.h delete mode 100644 src/libsystemd/sd-device/device-private.c delete mode 100644 src/libsystemd/sd-device/device-private.h delete mode 100644 src/libsystemd/sd-device/device-util.h delete mode 100644 src/libsystemd/sd-device/sd-device.c delete mode 120000 src/libsystemd/sd-event/Makefile delete mode 100644 src/libsystemd/sd-event/sd-event.c delete mode 100644 src/libsystemd/sd-event/test-event.c delete mode 120000 src/libsystemd/sd-hwdb/Makefile delete mode 100644 src/libsystemd/sd-hwdb/hwdb-internal.h delete mode 100644 src/libsystemd/sd-hwdb/hwdb-util.h delete mode 100644 src/libsystemd/sd-hwdb/sd-hwdb.c delete mode 120000 src/libsystemd/sd-id128/Makefile delete mode 100644 src/libsystemd/sd-id128/sd-id128.c delete mode 120000 src/libsystemd/sd-login/Makefile delete mode 100644 src/libsystemd/sd-login/sd-login.c delete mode 100644 src/libsystemd/sd-login/test-login.c delete mode 120000 src/libsystemd/sd-netlink/Makefile delete mode 100644 src/libsystemd/sd-netlink/local-addresses.c delete mode 100644 src/libsystemd/sd-netlink/local-addresses.h delete mode 100644 src/libsystemd/sd-netlink/netlink-internal.h delete mode 100644 src/libsystemd/sd-netlink/netlink-message.c delete mode 100644 src/libsystemd/sd-netlink/netlink-socket.c delete mode 100644 src/libsystemd/sd-netlink/netlink-types.c delete mode 100644 src/libsystemd/sd-netlink/netlink-types.h delete mode 100644 src/libsystemd/sd-netlink/netlink-util.c delete mode 100644 src/libsystemd/sd-netlink/netlink-util.h delete mode 100644 src/libsystemd/sd-netlink/rtnl-message.c delete mode 100644 src/libsystemd/sd-netlink/sd-netlink.c delete mode 100644 src/libsystemd/sd-netlink/test-local-addresses.c delete mode 100644 src/libsystemd/sd-netlink/test-netlink.c delete mode 120000 src/libsystemd/sd-network/Makefile delete mode 100644 src/libsystemd/sd-network/network-util.c delete mode 100644 src/libsystemd/sd-network/network-util.h delete mode 100644 src/libsystemd/sd-network/sd-network.c delete mode 120000 src/libsystemd/sd-path/Makefile delete mode 100644 src/libsystemd/sd-path/sd-path.c delete mode 120000 src/libsystemd/sd-resolve/Makefile delete mode 100644 src/libsystemd/sd-resolve/sd-resolve.c delete mode 100644 src/libsystemd/sd-resolve/test-resolve.c delete mode 120000 src/libsystemd/sd-utf8/Makefile delete mode 100644 src/libsystemd/sd-utf8/sd-utf8.c create mode 100644 src/libsystemd/src/Makefile create mode 100644 src/libsystemd/src/sd-bus/DIFFERENCES create mode 100644 src/libsystemd/src/sd-bus/GVARIANT-SERIALIZATION create mode 100644 src/libsystemd/src/sd-bus/PORTING-DBUS1 create mode 100644 src/libsystemd/src/sd-bus/bus-bloom.c create mode 100644 src/libsystemd/src/sd-bus/bus-bloom.h create mode 100644 src/libsystemd/src/sd-bus/bus-common-errors.c create mode 100644 src/libsystemd/src/sd-bus/bus-common-errors.h create mode 100644 src/libsystemd/src/sd-bus/bus-container.c create mode 100644 src/libsystemd/src/sd-bus/bus-container.h create mode 100644 src/libsystemd/src/sd-bus/bus-control.c create mode 100644 src/libsystemd/src/sd-bus/bus-control.h create mode 100644 src/libsystemd/src/sd-bus/bus-convenience.c create mode 100644 src/libsystemd/src/sd-bus/bus-creds.c create mode 100644 src/libsystemd/src/sd-bus/bus-creds.h create mode 100644 src/libsystemd/src/sd-bus/bus-dump.c create mode 100644 src/libsystemd/src/sd-bus/bus-dump.h create mode 100644 src/libsystemd/src/sd-bus/bus-error.c create mode 100644 src/libsystemd/src/sd-bus/bus-error.h create mode 100644 src/libsystemd/src/sd-bus/bus-gvariant.c create mode 100644 src/libsystemd/src/sd-bus/bus-gvariant.h create mode 100644 src/libsystemd/src/sd-bus/bus-internal.c create mode 100644 src/libsystemd/src/sd-bus/bus-internal.h create mode 100644 src/libsystemd/src/sd-bus/bus-introspect.c create mode 100644 src/libsystemd/src/sd-bus/bus-introspect.h create mode 100644 src/libsystemd/src/sd-bus/bus-kernel.c create mode 100644 src/libsystemd/src/sd-bus/bus-kernel.h create mode 100644 src/libsystemd/src/sd-bus/bus-match.c create mode 100644 src/libsystemd/src/sd-bus/bus-match.h create mode 100644 src/libsystemd/src/sd-bus/bus-message.c create mode 100644 src/libsystemd/src/sd-bus/bus-message.h create mode 100644 src/libsystemd/src/sd-bus/bus-objects.c create mode 100644 src/libsystemd/src/sd-bus/bus-objects.h create mode 100644 src/libsystemd/src/sd-bus/bus-protocol.h create mode 100644 src/libsystemd/src/sd-bus/bus-signature.c create mode 100644 src/libsystemd/src/sd-bus/bus-signature.h create mode 100644 src/libsystemd/src/sd-bus/bus-slot.c create mode 100644 src/libsystemd/src/sd-bus/bus-slot.h create mode 100644 src/libsystemd/src/sd-bus/bus-socket.c create mode 100644 src/libsystemd/src/sd-bus/bus-socket.h create mode 100644 src/libsystemd/src/sd-bus/bus-track.c create mode 100644 src/libsystemd/src/sd-bus/bus-track.h create mode 100644 src/libsystemd/src/sd-bus/bus-type.c create mode 100644 src/libsystemd/src/sd-bus/bus-type.h create mode 100644 src/libsystemd/src/sd-bus/kdbus.h create mode 100644 src/libsystemd/src/sd-bus/sd-bus.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-benchmark.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-chat.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-cleanup.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-creds.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-error.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-gvariant.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-introspect.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-kernel-bloom.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-kernel.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-marshal.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-match.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-objects.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-server.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-signature.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-zero-copy.c create mode 100644 src/libsystemd/src/sd-daemon/sd-daemon.c create mode 100644 src/libsystemd/src/sd-device/device-enumerator-private.h create mode 100644 src/libsystemd/src/sd-device/device-enumerator.c create mode 100644 src/libsystemd/src/sd-device/device-internal.h create mode 100644 src/libsystemd/src/sd-device/device-private.c create mode 100644 src/libsystemd/src/sd-device/device-private.h create mode 100644 src/libsystemd/src/sd-device/device-util.h create mode 100644 src/libsystemd/src/sd-device/sd-device.c create mode 100644 src/libsystemd/src/sd-event/sd-event.c create mode 100644 src/libsystemd/src/sd-event/test-event.c create mode 100644 src/libsystemd/src/sd-hwdb/hwdb-internal.h create mode 100644 src/libsystemd/src/sd-hwdb/hwdb-util.h create mode 100644 src/libsystemd/src/sd-hwdb/sd-hwdb.c create mode 100644 src/libsystemd/src/sd-id128/sd-id128.c create mode 100644 src/libsystemd/src/sd-journal/Makefile create mode 100644 src/libsystemd/src/sd-journal/audit-type.c create mode 100644 src/libsystemd/src/sd-journal/audit-type.h create mode 100644 src/libsystemd/src/sd-journal/catalog.c create mode 100644 src/libsystemd/src/sd-journal/catalog.h create mode 100644 src/libsystemd/src/sd-journal/compress.c create mode 100644 src/libsystemd/src/sd-journal/compress.h create mode 100644 src/libsystemd/src/sd-journal/fsprg.c create mode 100644 src/libsystemd/src/sd-journal/fsprg.h create mode 100644 src/libsystemd/src/sd-journal/journal-authenticate.c create mode 100644 src/libsystemd/src/sd-journal/journal-authenticate.h create mode 100644 src/libsystemd/src/sd-journal/journal-def.h create mode 100644 src/libsystemd/src/sd-journal/journal-file.c create mode 100644 src/libsystemd/src/sd-journal/journal-file.h create mode 100644 src/libsystemd/src/sd-journal/journal-internal.h create mode 100644 src/libsystemd/src/sd-journal/journal-send.c create mode 100644 src/libsystemd/src/sd-journal/journal-vacuum.c create mode 100644 src/libsystemd/src/sd-journal/journal-vacuum.h create mode 100644 src/libsystemd/src/sd-journal/journal-verify.c create mode 100644 src/libsystemd/src/sd-journal/journal-verify.h create mode 100644 src/libsystemd/src/sd-journal/lookup3.c create mode 100644 src/libsystemd/src/sd-journal/lookup3.h create mode 100644 src/libsystemd/src/sd-journal/mmap-cache.c create mode 100644 src/libsystemd/src/sd-journal/mmap-cache.h create mode 100644 src/libsystemd/src/sd-journal/sd-journal.c create mode 100644 src/libsystemd/src/sd-login/sd-login.c create mode 100644 src/libsystemd/src/sd-login/test-login.c create mode 100644 src/libsystemd/src/sd-netlink/local-addresses.c create mode 100644 src/libsystemd/src/sd-netlink/local-addresses.h create mode 100644 src/libsystemd/src/sd-netlink/netlink-internal.h create mode 100644 src/libsystemd/src/sd-netlink/netlink-message.c create mode 100644 src/libsystemd/src/sd-netlink/netlink-socket.c create mode 100644 src/libsystemd/src/sd-netlink/netlink-types.c create mode 100644 src/libsystemd/src/sd-netlink/netlink-types.h create mode 100644 src/libsystemd/src/sd-netlink/netlink-util.c create mode 100644 src/libsystemd/src/sd-netlink/netlink-util.h create mode 100644 src/libsystemd/src/sd-netlink/rtnl-message.c create mode 100644 src/libsystemd/src/sd-netlink/sd-netlink.c create mode 100644 src/libsystemd/src/sd-netlink/test-local-addresses.c create mode 100644 src/libsystemd/src/sd-netlink/test-netlink.c create mode 100644 src/libsystemd/src/sd-network/network-util.c create mode 100644 src/libsystemd/src/sd-network/network-util.h create mode 100644 src/libsystemd/src/sd-network/sd-network.c create mode 100644 src/libsystemd/src/sd-path/sd-path.c create mode 100644 src/libsystemd/src/sd-resolve/sd-resolve.c create mode 100644 src/libsystemd/src/sd-resolve/test-resolve.c create mode 100644 src/libsystemd/src/sd-utf8/sd-utf8.c create mode 100644 src/libsystemd/src/subdir.mk (limited to 'src/libsystemd') diff --git a/src/libsystemd/Makefile b/src/libsystemd/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd/Makefile b/src/libsystemd/Makefile new file mode 100644 index 0000000000..2c6505918b --- /dev/null +++ b/src/libsystemd/Makefile @@ -0,0 +1,110 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# 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 . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +LIBSYSTEMD_CURRENT=15 +LIBSYSTEMD_REVISION=0 +LIBSYSTEMD_AGE=15 + +EXTRA_DIST += \ + src/libsystemd/libsystemd.pc.in \ + src/libsystemd/sd-bus/DIFFERENCES \ + src/libsystemd/sd-bus/GVARIANT-SERIALIZATION + +libsystemd_la_SOURCES = \ + $(libsystemd_internal_la_SOURCES) \ + $(libsystemd_journal_internal_la_SOURCES) + +nodist_libsystemd_la_SOURCES = \ + $(nodist_libsystemd_internal_la_SOURCES) + +libsystemd_la_CFLAGS = \ + $(libsystemd_internal_la_CFLAGS) \ + $(libsystemd_journal_internal_la_CFLAGS) + +libsystemd_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + -version-info $(LIBSYSTEMD_CURRENT):$(LIBSYSTEMD_REVISION):$(LIBSYSTEMD_AGE) \ + -Wl,--version-script=$(srcdir)/libsystemd.sym + +libsystemd_la_LIBADD = \ + $(libsystemd_internal_la_LIBADD) \ + $(libsystemd_journal_internal_la_LIBADD) + +pkgconfiglib_DATA += \ + src/libsystemd/libsystemd.pc + +pkginclude_HEADERS += \ + src/systemd/sd-bus.h \ + src/systemd/sd-bus-protocol.h \ + src/systemd/sd-bus-vtable.h \ + src/systemd/sd-event.h \ + src/systemd/sd-login.h \ + src/systemd/sd-id128.h \ + src/systemd/sd-daemon.h + +lib_LTLIBRARIES += \ + libsystemd.la + +# ------------------------------------------------------------------------------ + +tests += \ + test-bus-marshal \ + test-bus-signature \ + test-bus-benchmark \ + test-bus-chat \ + test-bus-cleanup \ + test-bus-server \ + test-bus-match \ + test-bus-kernel \ + test-bus-kernel-bloom \ + test-bus-zero-copy \ + test-bus-introspect \ + test-bus-objects \ + test-bus-error \ + test-bus-creds \ + test-bus-gvariant \ + test-event \ + test-netlink \ + test-local-addresses \ + test-resolve + +test-libsystemd-sym.c: \ + $(top_builddir)/src/libsystemd/libsystemd.sym \ + src/systemd/sd-journal.h \ + src/systemd/sd-daemon.h \ + src/systemd/sd-login.h \ + src/systemd/sd-bus.h \ + src/systemd/sd-utf8.h \ + src/systemd/sd-resolve.h \ + src/systemd/sd-path.h \ + src/systemd/sd-event.h + $(generate-sym-test) + +nodist_test_libsystemd_sym_SOURCES = \ + test-libsystemd-sym.c +test_libsystemd_sym_LDADD = \ + libsystemd.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libsystemd/include/systemd/_sd-common.h b/src/libsystemd/include/systemd/_sd-common.h new file mode 100644 index 0000000000..3bb886be75 --- /dev/null +++ b/src/libsystemd/include/systemd/_sd-common.h @@ -0,0 +1,83 @@ +#ifndef foosdcommonhfoo +#define foosdcommonhfoo + +/*** + 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 . +***/ + +/* This is a private header; never even think of including this directly! */ + +#if __INCLUDE_LEVEL__ <= 1 +#error "Do not include _sd-common.h directly; it is a private header." +#endif + +#ifndef _sd_printf_ +# if __GNUC__ >= 4 +# define _sd_printf_(a,b) __attribute__ ((format (printf, a, b))) +# else +# define _sd_printf_(a,b) +# endif +#endif + +#ifndef _sd_sentinel_ +# define _sd_sentinel_ __attribute__((sentinel)) +#endif + +#ifndef _sd_packed_ +# define _sd_packed_ __attribute__((packed)) +#endif + +#ifndef _sd_pure_ +# define _sd_pure_ __attribute__((pure)) +#endif + +#ifndef _SD_STRINGIFY +# define _SD_XSTRINGIFY(x) #x +# define _SD_STRINGIFY(x) _SD_XSTRINGIFY(x) +#endif + +#ifndef _SD_BEGIN_DECLARATIONS +# ifdef __cplusplus +# define _SD_BEGIN_DECLARATIONS \ + extern "C" { \ + struct _sd_useless_struct_to_allow_trailing_semicolon_ +# else +# define _SD_BEGIN_DECLARATIONS \ + struct _sd_useless_struct_to_allow_trailing_semicolon_ +# endif +#endif + +#ifndef _SD_END_DECLARATIONS +# ifdef __cplusplus +# define _SD_END_DECLARATIONS \ + } \ + struct _sd_useless_cpp_struct_to_allow_trailing_semicolon_ +# else +# define _SD_END_DECLARATIONS \ + struct _sd_useless_struct_to_allow_trailing_semicolon_ +# endif +#endif + +#define _SD_DEFINE_POINTER_CLEANUP_FUNC(type, func) \ + static __inline__ void func##p(type **p) { \ + if (*p) \ + func(*p); \ + } \ + struct _sd_useless_struct_to_allow_trailing_semicolon_ + +#endif diff --git a/src/libsystemd/include/systemd/sd-bus-protocol.h b/src/libsystemd/include/systemd/sd-bus-protocol.h new file mode 100644 index 0000000000..623cee0c50 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-bus-protocol.h @@ -0,0 +1,102 @@ +#ifndef foosdbusprotocolhfoo +#define foosdbusprotocolhfoo + +/*** + 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 . +***/ + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* Types of message */ + +enum { + _SD_BUS_MESSAGE_TYPE_INVALID = 0, + SD_BUS_MESSAGE_METHOD_CALL, + SD_BUS_MESSAGE_METHOD_RETURN, + SD_BUS_MESSAGE_METHOD_ERROR, + SD_BUS_MESSAGE_SIGNAL, + _SD_BUS_MESSAGE_TYPE_MAX +}; + +/* Primitive types */ + +enum { + _SD_BUS_TYPE_INVALID = 0, + SD_BUS_TYPE_BYTE = 'y', + SD_BUS_TYPE_BOOLEAN = 'b', + SD_BUS_TYPE_INT16 = 'n', + SD_BUS_TYPE_UINT16 = 'q', + SD_BUS_TYPE_INT32 = 'i', + SD_BUS_TYPE_UINT32 = 'u', + SD_BUS_TYPE_INT64 = 'x', + SD_BUS_TYPE_UINT64 = 't', + SD_BUS_TYPE_DOUBLE = 'd', + SD_BUS_TYPE_STRING = 's', + SD_BUS_TYPE_OBJECT_PATH = 'o', + SD_BUS_TYPE_SIGNATURE = 'g', + SD_BUS_TYPE_UNIX_FD = 'h', + SD_BUS_TYPE_ARRAY = 'a', + SD_BUS_TYPE_VARIANT = 'v', + SD_BUS_TYPE_STRUCT = 'r', /* not actually used in signatures */ + SD_BUS_TYPE_STRUCT_BEGIN = '(', + SD_BUS_TYPE_STRUCT_END = ')', + SD_BUS_TYPE_DICT_ENTRY = 'e', /* not actually used in signatures */ + SD_BUS_TYPE_DICT_ENTRY_BEGIN = '{', + SD_BUS_TYPE_DICT_ENTRY_END = '}' +}; + +/* Well-known errors. Note that this is only a sanitized subset of the + * errors that the reference implementation generates. */ + +#define SD_BUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed" +#define SD_BUS_ERROR_NO_MEMORY "org.freedesktop.DBus.Error.NoMemory" +#define SD_BUS_ERROR_SERVICE_UNKNOWN "org.freedesktop.DBus.Error.ServiceUnknown" +#define SD_BUS_ERROR_NAME_HAS_NO_OWNER "org.freedesktop.DBus.Error.NameHasNoOwner" +#define SD_BUS_ERROR_NO_REPLY "org.freedesktop.DBus.Error.NoReply" +#define SD_BUS_ERROR_IO_ERROR "org.freedesktop.DBus.Error.IOError" +#define SD_BUS_ERROR_BAD_ADDRESS "org.freedesktop.DBus.Error.BadAddress" +#define SD_BUS_ERROR_NOT_SUPPORTED "org.freedesktop.DBus.Error.NotSupported" +#define SD_BUS_ERROR_LIMITS_EXCEEDED "org.freedesktop.DBus.Error.LimitsExceeded" +#define SD_BUS_ERROR_ACCESS_DENIED "org.freedesktop.DBus.Error.AccessDenied" +#define SD_BUS_ERROR_AUTH_FAILED "org.freedesktop.DBus.Error.AuthFailed" +#define SD_BUS_ERROR_NO_SERVER "org.freedesktop.DBus.Error.NoServer" +#define SD_BUS_ERROR_TIMEOUT "org.freedesktop.DBus.Error.Timeout" +#define SD_BUS_ERROR_NO_NETWORK "org.freedesktop.DBus.Error.NoNetwork" +#define SD_BUS_ERROR_ADDRESS_IN_USE "org.freedesktop.DBus.Error.AddressInUse" +#define SD_BUS_ERROR_DISCONNECTED "org.freedesktop.DBus.Error.Disconnected" +#define SD_BUS_ERROR_INVALID_ARGS "org.freedesktop.DBus.Error.InvalidArgs" +#define SD_BUS_ERROR_FILE_NOT_FOUND "org.freedesktop.DBus.Error.FileNotFound" +#define SD_BUS_ERROR_FILE_EXISTS "org.freedesktop.DBus.Error.FileExists" +#define SD_BUS_ERROR_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod" +#define SD_BUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject" +#define SD_BUS_ERROR_UNKNOWN_INTERFACE "org.freedesktop.DBus.Error.UnknownInterface" +#define SD_BUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty" +#define SD_BUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly" +#define SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN "org.freedesktop.DBus.Error.UnixProcessIdUnknown" +#define SD_BUS_ERROR_INVALID_SIGNATURE "org.freedesktop.DBus.Error.InvalidSignature" +#define SD_BUS_ERROR_INCONSISTENT_MESSAGE "org.freedesktop.DBus.Error.InconsistentMessage" +#define SD_BUS_ERROR_MATCH_RULE_NOT_FOUND "org.freedesktop.DBus.Error.MatchRuleNotFound" +#define SD_BUS_ERROR_MATCH_RULE_INVALID "org.freedesktop.DBus.Error.MatchRuleInvalid" +#define SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED \ + "org.freedesktop.DBus.Error.InteractiveAuthorizationRequired" + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-bus-vtable.h b/src/libsystemd/include/systemd/sd-bus-vtable.h new file mode 100644 index 0000000000..2b684b5678 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-bus-vtable.h @@ -0,0 +1,141 @@ +#ifndef foosdbusvtablehfoo +#define foosdbusvtablehfoo + +/*** + 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 . +***/ + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_bus_vtable sd_bus_vtable; + +#include + +enum { + _SD_BUS_VTABLE_START = '<', + _SD_BUS_VTABLE_END = '>', + _SD_BUS_VTABLE_METHOD = 'M', + _SD_BUS_VTABLE_SIGNAL = 'S', + _SD_BUS_VTABLE_PROPERTY = 'P', + _SD_BUS_VTABLE_WRITABLE_PROPERTY = 'W' +}; + +enum { + SD_BUS_VTABLE_DEPRECATED = 1ULL << 0, + SD_BUS_VTABLE_HIDDEN = 1ULL << 1, + SD_BUS_VTABLE_UNPRIVILEGED = 1ULL << 2, + SD_BUS_VTABLE_METHOD_NO_REPLY = 1ULL << 3, + SD_BUS_VTABLE_PROPERTY_CONST = 1ULL << 4, + SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE = 1ULL << 5, + SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION = 1ULL << 6, + SD_BUS_VTABLE_PROPERTY_EXPLICIT = 1ULL << 7, + _SD_BUS_VTABLE_CAPABILITY_MASK = 0xFFFFULL << 40 +}; + +#define SD_BUS_VTABLE_CAPABILITY(x) ((uint64_t) (((x)+1) & 0xFFFF) << 40) + +struct sd_bus_vtable { + /* Please do not initialize this structure directly, use the + * macros below instead */ + + uint8_t type:8; + uint64_t flags:56; + union { + struct { + size_t element_size; + } start; + struct { + const char *member; + const char *signature; + const char *result; + sd_bus_message_handler_t handler; + size_t offset; + } method; + struct { + const char *member; + const char *signature; + } signal; + struct { + const char *member; + const char *signature; + sd_bus_property_get_t get; + sd_bus_property_set_t set; + size_t offset; + } property; + } x; +}; + +#define SD_BUS_VTABLE_START(_flags) \ + { \ + .type = _SD_BUS_VTABLE_START, \ + .flags = _flags, \ + .x.start.element_size = sizeof(sd_bus_vtable), \ + } + +#define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags) \ + { \ + .type = _SD_BUS_VTABLE_METHOD, \ + .flags = _flags, \ + .x.method.member = _member, \ + .x.method.signature = _signature, \ + .x.method.result = _result, \ + .x.method.handler = _handler, \ + .x.method.offset = _offset, \ + } +#define SD_BUS_METHOD(_member, _signature, _result, _handler, _flags) \ + SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, 0, _flags) + +#define SD_BUS_SIGNAL(_member, _signature, _flags) \ + { \ + .type = _SD_BUS_VTABLE_SIGNAL, \ + .flags = _flags, \ + .x.signal.member = _member, \ + .x.signal.signature = _signature, \ + } + +#define SD_BUS_PROPERTY(_member, _signature, _get, _offset, _flags) \ + { \ + .type = _SD_BUS_VTABLE_PROPERTY, \ + .flags = _flags, \ + .x.property.member = _member, \ + .x.property.signature = _signature, \ + .x.property.get = _get, \ + .x.property.offset = _offset, \ + } + +#define SD_BUS_WRITABLE_PROPERTY(_member, _signature, _get, _set, _offset, _flags) \ + { \ + .type = _SD_BUS_VTABLE_WRITABLE_PROPERTY, \ + .flags = _flags, \ + .x.property.member = _member, \ + .x.property.signature = _signature, \ + .x.property.get = _get, \ + .x.property.set = _set, \ + .x.property.offset = _offset, \ + } + +#define SD_BUS_VTABLE_END \ + { \ + .type = _SD_BUS_VTABLE_END, \ + } + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-bus.h b/src/libsystemd/include/systemd/sd-bus.h new file mode 100644 index 0000000000..3c1b4b97a4 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-bus.h @@ -0,0 +1,456 @@ +#ifndef foosdbushfoo +#define foosdbushfoo + +/*** + 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 . +***/ + +#include +#include +#include +#include + +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* Types */ + +typedef struct sd_bus sd_bus; +typedef struct sd_bus_message sd_bus_message; +typedef struct sd_bus_slot sd_bus_slot; +typedef struct sd_bus_creds sd_bus_creds; +typedef struct sd_bus_track sd_bus_track; + +typedef struct { + const char *name; + const char *message; + int _need_free; +} sd_bus_error; + +typedef struct { + const char* name; + int code; +} sd_bus_error_map; + +/* Flags */ + +enum { + SD_BUS_CREDS_PID = 1ULL << 0, + SD_BUS_CREDS_TID = 1ULL << 1, + SD_BUS_CREDS_PPID = 1ULL << 2, + SD_BUS_CREDS_UID = 1ULL << 3, + SD_BUS_CREDS_EUID = 1ULL << 4, + SD_BUS_CREDS_SUID = 1ULL << 5, + SD_BUS_CREDS_FSUID = 1ULL << 6, + SD_BUS_CREDS_GID = 1ULL << 7, + SD_BUS_CREDS_EGID = 1ULL << 8, + SD_BUS_CREDS_SGID = 1ULL << 9, + SD_BUS_CREDS_FSGID = 1ULL << 10, + SD_BUS_CREDS_SUPPLEMENTARY_GIDS = 1ULL << 11, + SD_BUS_CREDS_COMM = 1ULL << 12, + SD_BUS_CREDS_TID_COMM = 1ULL << 13, + SD_BUS_CREDS_EXE = 1ULL << 14, + SD_BUS_CREDS_CMDLINE = 1ULL << 15, + SD_BUS_CREDS_CGROUP = 1ULL << 16, + SD_BUS_CREDS_UNIT = 1ULL << 17, + SD_BUS_CREDS_SLICE = 1ULL << 18, + SD_BUS_CREDS_USER_UNIT = 1ULL << 19, + SD_BUS_CREDS_USER_SLICE = 1ULL << 20, + SD_BUS_CREDS_SESSION = 1ULL << 21, + SD_BUS_CREDS_OWNER_UID = 1ULL << 22, + SD_BUS_CREDS_EFFECTIVE_CAPS = 1ULL << 23, + SD_BUS_CREDS_PERMITTED_CAPS = 1ULL << 24, + SD_BUS_CREDS_INHERITABLE_CAPS = 1ULL << 25, + SD_BUS_CREDS_BOUNDING_CAPS = 1ULL << 26, + SD_BUS_CREDS_SELINUX_CONTEXT = 1ULL << 27, + SD_BUS_CREDS_AUDIT_SESSION_ID = 1ULL << 28, + SD_BUS_CREDS_AUDIT_LOGIN_UID = 1ULL << 29, + SD_BUS_CREDS_TTY = 1ULL << 30, + SD_BUS_CREDS_UNIQUE_NAME = 1ULL << 31, + SD_BUS_CREDS_WELL_KNOWN_NAMES = 1ULL << 32, + SD_BUS_CREDS_DESCRIPTION = 1ULL << 33, + SD_BUS_CREDS_AUGMENT = 1ULL << 63, /* special flag, if on sd-bus will augment creds struct, in a potentially race-full way. */ + _SD_BUS_CREDS_ALL = (1ULL << 34) -1 +}; + +enum { + SD_BUS_NAME_REPLACE_EXISTING = 1ULL << 0, + SD_BUS_NAME_ALLOW_REPLACEMENT = 1ULL << 1, + SD_BUS_NAME_QUEUE = 1ULL << 2 +}; + +/* Callbacks */ + +typedef int (*sd_bus_message_handler_t)(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); +typedef int (*sd_bus_property_get_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); +typedef int (*sd_bus_property_set_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *ret_error); +typedef int (*sd_bus_object_find_t) (sd_bus *bus, const char *path, const char *interface, void *userdata, void **ret_found, sd_bus_error *ret_error); +typedef int (*sd_bus_node_enumerator_t) (sd_bus *bus, const char *prefix, void *userdata, char ***ret_nodes, sd_bus_error *ret_error); +typedef int (*sd_bus_track_handler_t) (sd_bus_track *track, void *userdata); + +#include +#include + +/* Connections */ + +int sd_bus_default(sd_bus **ret); +int sd_bus_default_user(sd_bus **ret); +int sd_bus_default_system(sd_bus **ret); + +int sd_bus_open(sd_bus **ret); +int sd_bus_open_user(sd_bus **ret); +int sd_bus_open_system(sd_bus **ret); +int sd_bus_open_system_remote(sd_bus **ret, const char *host); +int sd_bus_open_system_machine(sd_bus **ret, const char *machine); + +int sd_bus_new(sd_bus **ret); + +int sd_bus_set_address(sd_bus *bus, const char *address); +int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd); +int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]); +int sd_bus_get_address(sd_bus *bus, const char **address); +int sd_bus_set_bus_client(sd_bus *bus, int b); +int sd_bus_is_bus_client(sd_bus *bus); +int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t bus_id); +int sd_bus_is_server(sd_bus *bus); +int sd_bus_set_anonymous(sd_bus *bus, int b); +int sd_bus_is_anonymous(sd_bus *bus); +int sd_bus_set_trusted(sd_bus *bus, int b); +int sd_bus_is_trusted(sd_bus *bus); +int sd_bus_set_monitor(sd_bus *bus, int b); +int sd_bus_is_monitor(sd_bus *bus); +int sd_bus_set_description(sd_bus *bus, const char *description); +int sd_bus_get_description(sd_bus *bus, const char **description); +int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t creds_mask); +int sd_bus_negotiate_timestamp(sd_bus *bus, int b); +int sd_bus_negotiate_fds(sd_bus *bus, int b); +int sd_bus_can_send(sd_bus *bus, char type); +int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *creds_mask); +int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b); +int sd_bus_get_allow_interactive_authorization(sd_bus *bus); + +int sd_bus_start(sd_bus *ret); + +int sd_bus_try_close(sd_bus *bus); +void sd_bus_close(sd_bus *bus); + +sd_bus *sd_bus_ref(sd_bus *bus); +sd_bus *sd_bus_unref(sd_bus *bus); +sd_bus *sd_bus_flush_close_unref(sd_bus *bus); + +void sd_bus_default_flush_close(void); + +int sd_bus_is_open(sd_bus *bus); + +int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id); +int sd_bus_get_scope(sd_bus *bus, const char **scope); +int sd_bus_get_tid(sd_bus *bus, pid_t *tid); +int sd_bus_get_owner_creds(sd_bus *bus, uint64_t creds_mask, sd_bus_creds **ret); + +int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie); +int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie); +int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply); +int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec); + +int sd_bus_get_fd(sd_bus *bus); +int sd_bus_get_events(sd_bus *bus); +int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec); +int sd_bus_process(sd_bus *bus, sd_bus_message **r); +int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r); +int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec); +int sd_bus_flush(sd_bus *bus); + +sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus); +sd_bus_message* sd_bus_get_current_message(sd_bus *bus); +sd_bus_message_handler_t sd_bus_get_current_handler(sd_bus *bus); +void* sd_bus_get_current_userdata(sd_bus *bus); + +int sd_bus_attach_event(sd_bus *bus, sd_event *e, int priority); +int sd_bus_detach_event(sd_bus *bus); +sd_event *sd_bus_get_event(sd_bus *bus); + +int sd_bus_add_filter(sd_bus *bus, sd_bus_slot **slot, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_object(sd_bus *bus, sd_bus_slot **slot, const char *path, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_fallback(sd_bus *bus, sd_bus_slot **slot, const char *prefix, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata); +int sd_bus_add_fallback_vtable(sd_bus *bus, sd_bus_slot **slot, const char *prefix, const char *interface, const sd_bus_vtable *vtable, sd_bus_object_find_t find, void *userdata); +int sd_bus_add_node_enumerator(sd_bus *bus, sd_bus_slot **slot, const char *path, sd_bus_node_enumerator_t callback, void *userdata); +int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path); + +/* Slot object */ + +sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot); +sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot); + +sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot); +void *sd_bus_slot_get_userdata(sd_bus_slot *slot); +void *sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata); +int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description); +int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description); + +sd_bus_message* sd_bus_slot_get_current_message(sd_bus_slot *slot); +sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *bus); +void *sd_bus_slot_get_current_userdata(sd_bus_slot *slot); + +/* Message object */ + +int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member); +int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member); +int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m); +int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e); +int sd_bus_message_new_method_errorf(sd_bus_message *call, sd_bus_message **m, const char *name, const char *format, ...) _sd_printf_(4, 5); +int sd_bus_message_new_method_errno(sd_bus_message *call, sd_bus_message **m, int error, const sd_bus_error *e); +int sd_bus_message_new_method_errnof(sd_bus_message *call, sd_bus_message **m, int error, const char *format, ...) _sd_printf_(4, 5); + +sd_bus_message* sd_bus_message_ref(sd_bus_message *m); +sd_bus_message* sd_bus_message_unref(sd_bus_message *m); + +int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type); +int sd_bus_message_get_cookie(sd_bus_message *m, uint64_t *cookie); +int sd_bus_message_get_reply_cookie(sd_bus_message *m, uint64_t *cookie); +int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority); + +int sd_bus_message_get_expect_reply(sd_bus_message *m); +int sd_bus_message_get_auto_start(sd_bus_message *m); +int sd_bus_message_get_allow_interactive_authorization(sd_bus_message *m); + +const char *sd_bus_message_get_signature(sd_bus_message *m, int complete); +const char *sd_bus_message_get_path(sd_bus_message *m); +const char *sd_bus_message_get_interface(sd_bus_message *m); +const char *sd_bus_message_get_member(sd_bus_message *m); +const char *sd_bus_message_get_destination(sd_bus_message *m); +const char *sd_bus_message_get_sender(sd_bus_message *m); +const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m); +int sd_bus_message_get_errno(sd_bus_message *m); + +int sd_bus_message_get_monotonic_usec(sd_bus_message *m, uint64_t *usec); +int sd_bus_message_get_realtime_usec(sd_bus_message *m, uint64_t *usec); +int sd_bus_message_get_seqnum(sd_bus_message *m, uint64_t* seqnum); + +sd_bus* sd_bus_message_get_bus(sd_bus_message *m); +sd_bus_creds *sd_bus_message_get_creds(sd_bus_message *m); /* do not unref the result */ + +int sd_bus_message_is_signal(sd_bus_message *m, const char *interface, const char *member); +int sd_bus_message_is_method_call(sd_bus_message *m, const char *interface, const char *member); +int sd_bus_message_is_method_error(sd_bus_message *m, const char *name); +int sd_bus_message_is_empty(sd_bus_message *m); +int sd_bus_message_has_signature(sd_bus_message *m, const char *signature); + +int sd_bus_message_set_expect_reply(sd_bus_message *m, int b); +int sd_bus_message_set_auto_start(sd_bus_message *m, int b); +int sd_bus_message_set_allow_interactive_authorization(sd_bus_message *m, int b); + +int sd_bus_message_set_destination(sd_bus_message *m, const char *destination); +int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority); + +int sd_bus_message_append(sd_bus_message *m, const char *types, ...); +int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p); +int sd_bus_message_append_array(sd_bus_message *m, char type, const void *ptr, size_t size); +int sd_bus_message_append_array_space(sd_bus_message *m, char type, size_t size, void **ptr); +int sd_bus_message_append_array_iovec(sd_bus_message *m, char type, const struct iovec *iov, unsigned n); +int sd_bus_message_append_array_memfd(sd_bus_message *m, char type, int memfd, uint64_t offset, uint64_t size); +int sd_bus_message_append_string_space(sd_bus_message *m, size_t size, char **s); +int sd_bus_message_append_string_iovec(sd_bus_message *m, const struct iovec *iov, unsigned n); +int sd_bus_message_append_string_memfd(sd_bus_message *m, int memfd, uint64_t offset, uint64_t size); +int sd_bus_message_append_strv(sd_bus_message *m, char **l); +int sd_bus_message_open_container(sd_bus_message *m, char type, const char *contents); +int sd_bus_message_close_container(sd_bus_message *m); +int sd_bus_message_copy(sd_bus_message *m, sd_bus_message *source, int all); + +int sd_bus_message_read(sd_bus_message *m, const char *types, ...); +int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p); +int sd_bus_message_read_array(sd_bus_message *m, char type, const void **ptr, size_t *size); +int sd_bus_message_read_strv(sd_bus_message *m, char ***l); /* free the result! */ +int sd_bus_message_skip(sd_bus_message *m, const char *types); +int sd_bus_message_enter_container(sd_bus_message *m, char type, const char *contents); +int sd_bus_message_exit_container(sd_bus_message *m); +int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char **contents); +int sd_bus_message_verify_type(sd_bus_message *m, char type, const char *contents); +int sd_bus_message_at_end(sd_bus_message *m, int complete); +int sd_bus_message_rewind(sd_bus_message *m, int complete); + +/* Bus management */ + +int sd_bus_get_unique_name(sd_bus *bus, const char **unique); +int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags); +int sd_bus_release_name(sd_bus *bus, const char *name); +int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable); /* free the results */ +int sd_bus_get_name_creds(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **creds); /* unref the result! */ +int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine); + +/* Convenience calls */ + +int sd_bus_call_method(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *types, ...); +int sd_bus_call_method_async(sd_bus *bus, sd_bus_slot **slot, const char *destination, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata, const char *types, ...); +int sd_bus_get_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *type); +int sd_bus_get_property_trivial(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char type, void *ret_ptr); +int sd_bus_get_property_string(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char **ret); /* free the result! */ +int sd_bus_get_property_strv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char ***ret); /* free the result! */ +int sd_bus_set_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *type, ...); + +int sd_bus_reply_method_return(sd_bus_message *call, const char *types, ...); +int sd_bus_reply_method_error(sd_bus_message *call, const sd_bus_error *e); +int sd_bus_reply_method_errorf(sd_bus_message *call, const char *name, const char *format, ...) _sd_printf_(3, 4); +int sd_bus_reply_method_errno(sd_bus_message *call, int error, const sd_bus_error *e); +int sd_bus_reply_method_errnof(sd_bus_message *call, int error, const char *format, ...) _sd_printf_(3, 4); + +int sd_bus_emit_signal(sd_bus *bus, const char *path, const char *interface, const char *member, const char *types, ...); + +int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names); +int sd_bus_emit_properties_changed(sd_bus *bus, const char *path, const char *interface, const char *name, ...) _sd_sentinel_; + +int sd_bus_emit_object_added(sd_bus *bus, const char *path); +int sd_bus_emit_object_removed(sd_bus *bus, const char *path); +int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces); +int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_; +int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces); +int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_; + +int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds); +int sd_bus_query_sender_privilege(sd_bus_message *call, int capability); + +/* Credential handling */ + +int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t creds_mask); +sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c); +sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c); +uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c); +uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c); + +int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid); +int sd_bus_creds_get_ppid(sd_bus_creds *c, pid_t *ppid); +int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid); +int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid); +int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid); +int sd_bus_creds_get_suid(sd_bus_creds *c, uid_t *suid); +int sd_bus_creds_get_fsuid(sd_bus_creds *c, uid_t *fsuid); +int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid); +int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid); +int sd_bus_creds_get_sgid(sd_bus_creds *c, gid_t *sgid); +int sd_bus_creds_get_fsgid(sd_bus_creds *c, gid_t *fsgid); +int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids); +int sd_bus_creds_get_comm(sd_bus_creds *c, const char **comm); +int sd_bus_creds_get_tid_comm(sd_bus_creds *c, const char **comm); +int sd_bus_creds_get_exe(sd_bus_creds *c, const char **exe); +int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline); +int sd_bus_creds_get_cgroup(sd_bus_creds *c, const char **cgroup); +int sd_bus_creds_get_unit(sd_bus_creds *c, const char **unit); +int sd_bus_creds_get_slice(sd_bus_creds *c, const char **slice); +int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **unit); +int sd_bus_creds_get_user_slice(sd_bus_creds *c, const char **slice); +int sd_bus_creds_get_session(sd_bus_creds *c, const char **session); +int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid); +int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability); +int sd_bus_creds_has_permitted_cap(sd_bus_creds *c, int capability); +int sd_bus_creds_has_inheritable_cap(sd_bus_creds *c, int capability); +int sd_bus_creds_has_bounding_cap(sd_bus_creds *c, int capability); +int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **context); +int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid); +int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *loginuid); +int sd_bus_creds_get_tty(sd_bus_creds *c, const char **tty); +int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **name); +int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***names); +int sd_bus_creds_get_description(sd_bus_creds *c, const char **name); + +/* Error structures */ + +#define SD_BUS_ERROR_MAKE_CONST(name, message) ((const sd_bus_error) {(name), (message), 0}) +#define SD_BUS_ERROR_NULL SD_BUS_ERROR_MAKE_CONST(NULL, NULL) + +void sd_bus_error_free(sd_bus_error *e); +int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message); +int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) _sd_printf_(3, 4); +int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message); +int sd_bus_error_set_errno(sd_bus_error *e, int error); +int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) _sd_printf_(3, 4); +int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _sd_printf_(3,0); +int sd_bus_error_get_errno(const sd_bus_error *e); +int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e); +int sd_bus_error_is_set(const sd_bus_error *e); +int sd_bus_error_has_name(const sd_bus_error *e, const char *name); + +#define SD_BUS_ERROR_MAP(_name, _code) \ + { \ + .name = _name, \ + .code = _code, \ + } +#define SD_BUS_ERROR_MAP_END \ + { \ + .name = NULL, \ + .code = - 'x', \ + } + +int sd_bus_error_add_map(const sd_bus_error_map *map); + +/* Auxiliary macros */ + +#define SD_BUS_MESSAGE_APPEND_ID128(x) 16, \ + (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], \ + (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], \ + (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], \ + (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] + +#define SD_BUS_MESSAGE_READ_ID128(x) 16, \ + &(x).bytes[0], &(x).bytes[1], &(x).bytes[2], &(x).bytes[3], \ + &(x).bytes[4], &(x).bytes[5], &(x).bytes[6], &(x).bytes[7], \ + &(x).bytes[8], &(x).bytes[9], &(x).bytes[10], &(x).bytes[11], \ + &(x).bytes[12], &(x).bytes[13], &(x).bytes[14], &(x).bytes[15] + +/* Label escaping */ + +int sd_bus_path_encode(const char *prefix, const char *external_id, char **ret_path); +int sd_bus_path_encode_many(char **out, const char *path_template, ...); +int sd_bus_path_decode(const char *path, const char *prefix, char **ret_external_id); +int sd_bus_path_decode_many(const char *path, const char *path_template, ...); + +/* Tracking peers */ + +int sd_bus_track_new(sd_bus *bus, sd_bus_track **track, sd_bus_track_handler_t handler, void *userdata); +sd_bus_track* sd_bus_track_ref(sd_bus_track *track); +sd_bus_track* sd_bus_track_unref(sd_bus_track *track); + +sd_bus* sd_bus_track_get_bus(sd_bus_track *track); +void *sd_bus_track_get_userdata(sd_bus_track *track); +void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata); + +int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m); +int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m); +int sd_bus_track_add_name(sd_bus_track *track, const char *name); +int sd_bus_track_remove_name(sd_bus_track *track, const char *name); + +unsigned sd_bus_track_count(sd_bus_track *track); +const char* sd_bus_track_contains(sd_bus_track *track, const char *names); +const char* sd_bus_track_first(sd_bus_track *track); +const char* sd_bus_track_next(sd_bus_track *track); + +/* Define helpers so that __attribute__((cleanup(sd_bus_unrefp))) and similar may be used. */ +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus, sd_bus_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus, sd_bus_flush_close_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus_slot, sd_bus_slot_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus_message, sd_bus_message_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus_creds, sd_bus_creds_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus_track, sd_bus_track_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-daemon.h b/src/libsystemd/include/systemd/sd-daemon.h new file mode 100644 index 0000000000..e6787b0a64 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-daemon.h @@ -0,0 +1,289 @@ +#ifndef foosddaemonhfoo +#define foosddaemonhfoo + +/*** + 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 . +***/ + +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* + The following functionality is provided: + + - Support for logging with log levels on stderr + - File descriptor passing for socket-based activation + - Daemon startup and status notification + - Detection of systemd boots + + See sd-daemon(3) for more information. +*/ + +/* + Log levels for usage on stderr: + + fprintf(stderr, SD_NOTICE "Hello World!\n"); + + This is similar to printk() usage in the kernel. +*/ +#define SD_EMERG "<0>" /* system is unusable */ +#define SD_ALERT "<1>" /* action must be taken immediately */ +#define SD_CRIT "<2>" /* critical conditions */ +#define SD_ERR "<3>" /* error conditions */ +#define SD_WARNING "<4>" /* warning conditions */ +#define SD_NOTICE "<5>" /* normal but significant condition */ +#define SD_INFO "<6>" /* informational */ +#define SD_DEBUG "<7>" /* debug-level messages */ + +/* The first passed file descriptor is fd 3 */ +#define SD_LISTEN_FDS_START 3 + +/* + Returns how many file descriptors have been passed, or a negative + errno code on failure. Optionally, removes the $LISTEN_FDS and + $LISTEN_PID file descriptors from the environment (recommended, but + problematic in threaded environments). If r is the return value of + this function you'll find the file descriptors passed as fds + SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative + errno style error code on failure. This function call ensures that + the FD_CLOEXEC flag is set for the passed file descriptors, to make + sure they are not passed on to child processes. If FD_CLOEXEC shall + not be set, the caller needs to unset it after this call for all file + descriptors that are used. + + See sd_listen_fds(3) for more information. +*/ +int sd_listen_fds(int unset_environment); + +int sd_listen_fds_with_names(int unset_environment, char ***names); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a FIFO in the file system stored under the + specified path, 0 otherwise. If path is NULL a path name check will + not be done and the call only verifies if the file descriptor + refers to a FIFO. Returns a negative errno style error code on + failure. + + See sd_is_fifo(3) for more information. +*/ +int sd_is_fifo(int fd, const char *path); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a special character device on the file + system stored under the specified path, 0 otherwise. + If path is NULL a path name check will not be done and the call + only verifies if the file descriptor refers to a special character. + Returns a negative errno style error code on failure. + + See sd_is_special(3) for more information. +*/ +int sd_is_special(int fd, const char *path); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a socket of the specified family (AF_INET, + ...) and type (SOCK_DGRAM, SOCK_STREAM, ...), 0 otherwise. If + family is 0 a socket family check will not be done. If type is 0 a + socket type check will not be done and the call only verifies if + the file descriptor refers to a socket. If listening is > 0 it is + verified that the socket is in listening mode. (i.e. listen() has + been called) If listening is == 0 it is verified that the socket is + not in listening mode. If listening is < 0 no listening mode check + is done. Returns a negative errno style error code on failure. + + See sd_is_socket(3) for more information. +*/ +int sd_is_socket(int fd, int family, int type, int listening); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is an Internet socket, of the specified family + (either AF_INET or AF_INET6) and the specified type (SOCK_DGRAM, + SOCK_STREAM, ...), 0 otherwise. If version is 0 a protocol version + check is not done. If type is 0 a socket type check will not be + done. If port is 0 a socket port check will not be done. The + listening flag is used the same way as in sd_is_socket(). Returns a + negative errno style error code on failure. + + See sd_is_socket_inet(3) for more information. +*/ +int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is an AF_UNIX socket of the specified type + (SOCK_DGRAM, SOCK_STREAM, ...) and path, 0 otherwise. If type is 0 + a socket type check will not be done. If path is NULL a socket path + check will not be done. For normal AF_UNIX sockets set length to + 0. For abstract namespace sockets set length to the length of the + socket name (including the initial 0 byte), and pass the full + socket path in path (including the initial 0 byte). The listening + flag is used the same way as in sd_is_socket(). Returns a negative + errno style error code on failure. + + See sd_is_socket_unix(3) for more information. +*/ +int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a POSIX Message Queue of the specified name, + 0 otherwise. If path is NULL a message queue name check is not + done. Returns a negative errno style error code on failure. + + See sd_is_mq(3) for more information. +*/ +int sd_is_mq(int fd, const char *path); + +/* + Informs systemd about changed daemon state. This takes a number of + newline separated environment-style variable assignments in a + string. The following variables are known: + + READY=1 Tells systemd that daemon startup is finished (only + relevant for services of Type=notify). The passed + argument is a boolean "1" or "0". Since there is + little value in signaling non-readiness the only + value daemons should send is "READY=1". + + STATUS=... Passes a single-line status string back to systemd + that describes the daemon state. This is free-form + and can be used for various purposes: general state + feedback, fsck-like programs could pass completion + percentages and failing programs could pass a human + readable error message. Example: "STATUS=Completed + 66% of file system check..." + + ERRNO=... If a daemon fails, the errno-style error code, + formatted as string. Example: "ERRNO=2" for ENOENT. + + BUSERROR=... If a daemon fails, the D-Bus error-style error + code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut" + + MAINPID=... The main pid of a daemon, in case systemd did not + fork off the process itself. Example: "MAINPID=4711" + + WATCHDOG=1 Tells systemd to update the watchdog timestamp. + Services using this feature should do this in + regular intervals. A watchdog framework can use the + timestamps to detect failed services. Also see + sd_watchdog_enabled() below. + + FDSTORE=1 Store the file descriptors passed along with the + message in the per-service file descriptor store, + and pass them to the main process again on next + invocation. This variable is only supported with + sd_pid_notify_with_fds(). + + Daemons can choose to send additional variables. However, it is + recommended to prefix variable names not listed above with X_. + + Returns a negative errno-style error code on failure. Returns > 0 + if systemd could be notified, 0 if it couldn't possibly because + systemd is not running. + + Example: When a daemon finished starting up, it could issue this + call to notify systemd about it: + + sd_notify(0, "READY=1"); + + See sd_notifyf() for more complete examples. + + See sd_notify(3) for more information. +*/ +int sd_notify(int unset_environment, const char *state); + +/* + Similar to sd_notify() but takes a format string. + + Example 1: A daemon could send the following after initialization: + + sd_notifyf(0, "READY=1\n" + "STATUS=Processing requests...\n" + "MAINPID=%lu", + (unsigned long) getpid()); + + Example 2: A daemon could send the following shortly before + exiting, on failure: + + sd_notifyf(0, "STATUS=Failed to start up: %s\n" + "ERRNO=%i", + strerror(errno), + errno); + + See sd_notifyf(3) for more information. +*/ +int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_(2,3); + +/* + Similar to sd_notify(), but send the message on behalf of another + process, if the appropriate permissions are available. +*/ +int sd_pid_notify(pid_t pid, int unset_environment, const char *state); + +/* + Similar to sd_notifyf(), but send the message on behalf of another + process, if the appropriate permissions are available. +*/ +int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) _sd_printf_(3,4); + +/* + Similar to sd_pid_notify(), but also passes the specified fd array + to the service manager for storage. This is particularly useful for + FDSTORE=1 messages. +*/ +int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds); + +/* + Returns > 0 if the system was booted with systemd. Returns < 0 on + error. Returns 0 if the system was not booted with systemd. Note + that all of the functions above handle non-systemd boots just + fine. You should NOT protect them with a call to this function. Also + note that this function checks whether the system, not the user + session is controlled by systemd. However the functions above work + for both user and system services. + + See sd_booted(3) for more information. +*/ +int sd_booted(void); + +/* + Returns > 0 if the service manager expects watchdog keep-alive + events to be sent regularly via sd_notify(0, "WATCHDOG=1"). Returns + 0 if it does not expect this. If the usec argument is non-NULL + returns the watchdog timeout in µs after which the service manager + will act on a process that has not sent a watchdog keep alive + message. This function is useful to implement services that + recognize automatically if they are being run under supervision of + systemd with WatchdogSec= set. It is recommended for clients to + generate keep-alive pings via sd_notify(0, "WATCHDOG=1") every half + of the returned time. + + See sd_watchdog_enabled(3) for more information. +*/ +int sd_watchdog_enabled(int unset_environment, uint64_t *usec); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-device.h b/src/libsystemd/include/systemd/sd-device.h new file mode 100644 index 0000000000..c1d07561d7 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-device.h @@ -0,0 +1,101 @@ +#ifndef foosddevicehfoo +#define foosddevicehfoo + +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + Copyright 2014-2015 Tom Gundersen + + 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 . +***/ + +#include +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_device sd_device; +typedef struct sd_device_enumerator sd_device_enumerator; + +/* device */ + +sd_device *sd_device_ref(sd_device *device); +sd_device *sd_device_unref(sd_device *device); + +int sd_device_new_from_syspath(sd_device **ret, const char *syspath); +int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum); +int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname); +int sd_device_new_from_device_id(sd_device **ret, const char *id); + +int sd_device_get_parent(sd_device *child, sd_device **ret); +int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret); + +int sd_device_get_syspath(sd_device *device, const char **ret); +int sd_device_get_subsystem(sd_device *device, const char **ret); +int sd_device_get_devtype(sd_device *device, const char **ret); +int sd_device_get_devnum(sd_device *device, dev_t *devnum); +int sd_device_get_ifindex(sd_device *device, int *ifindex); +int sd_device_get_driver(sd_device *device, const char **ret); +int sd_device_get_devpath(sd_device *device, const char **ret); +int sd_device_get_devname(sd_device *device, const char **ret); +int sd_device_get_sysname(sd_device *device, const char **ret); +int sd_device_get_sysnum(sd_device *device, const char **ret); + +int sd_device_get_is_initialized(sd_device *device, int *initialized); +int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec); + +const char *sd_device_get_tag_first(sd_device *device); +const char *sd_device_get_tag_next(sd_device *device); +const char *sd_device_get_devlink_first(sd_device *device); +const char *sd_device_get_devlink_next(sd_device *device); +const char *sd_device_get_property_first(sd_device *device, const char **value); +const char *sd_device_get_property_next(sd_device *device, const char **value); +const char *sd_device_get_sysattr_first(sd_device *device); +const char *sd_device_get_sysattr_next(sd_device *device); + +int sd_device_has_tag(sd_device *device, const char *tag); +int sd_device_get_property_value(sd_device *device, const char *key, const char **value); +int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value); + +int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *value); + +/* device enumerator */ + +int sd_device_enumerator_new(sd_device_enumerator **ret); +sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator); +sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator); + +sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator); + +int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match); +int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match); +int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value); +int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname); +int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag); +int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent); +int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device, sd_device_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device_enumerator, sd_device_enumerator_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-dhcp-client.h b/src/libsystemd/include/systemd/sd-dhcp-client.h new file mode 100644 index 0000000000..f7bd5c4b7a --- /dev/null +++ b/src/libsystemd/include/systemd/sd-dhcp-client.h @@ -0,0 +1,158 @@ +#ifndef foosddhcpclienthfoo +#define foosddhcpclienthfoo + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + + 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 . +***/ + +#include +#include +#include +#include + +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + SD_DHCP_CLIENT_EVENT_STOP = 0, + SD_DHCP_CLIENT_EVENT_IP_ACQUIRE = 1, + SD_DHCP_CLIENT_EVENT_IP_CHANGE = 2, + SD_DHCP_CLIENT_EVENT_EXPIRED = 3, + SD_DHCP_CLIENT_EVENT_RENEW = 4, +}; + +enum { + SD_DHCP_OPTION_PAD = 0, + SD_DHCP_OPTION_SUBNET_MASK = 1, + SD_DHCP_OPTION_TIME_OFFSET = 2, + SD_DHCP_OPTION_ROUTER = 3, + SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6, + SD_DHCP_OPTION_HOST_NAME = 12, + SD_DHCP_OPTION_BOOT_FILE_SIZE = 13, + SD_DHCP_OPTION_DOMAIN_NAME = 15, + SD_DHCP_OPTION_ROOT_PATH = 17, + SD_DHCP_OPTION_ENABLE_IP_FORWARDING = 19, + SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20, + SD_DHCP_OPTION_POLICY_FILTER = 21, + SD_DHCP_OPTION_INTERFACE_MDR = 22, + SD_DHCP_OPTION_INTERFACE_TTL = 23, + SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24, + SD_DHCP_OPTION_INTERFACE_MTU = 26, + SD_DHCP_OPTION_BROADCAST = 28, + SD_DHCP_OPTION_STATIC_ROUTE = 33, + SD_DHCP_OPTION_NTP_SERVER = 42, + SD_DHCP_OPTION_VENDOR_SPECIFIC = 43, + SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, + SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, + SD_DHCP_OPTION_OVERLOAD = 52, + SD_DHCP_OPTION_MESSAGE_TYPE = 53, + SD_DHCP_OPTION_SERVER_IDENTIFIER = 54, + SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, + SD_DHCP_OPTION_ERROR_MESSAGE = 56, + SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, + SD_DHCP_OPTION_RENEWAL_T1_TIME = 58, + SD_DHCP_OPTION_REBINDING_T2_TIME = 59, + SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, + SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61, + SD_DHCP_OPTION_FQDN = 81, + SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, + SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, + SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, + SD_DHCP_OPTION_PRIVATE_BASE = 224, + SD_DHCP_OPTION_PRIVATE_LAST = 254, + SD_DHCP_OPTION_END = 255, +}; + +typedef struct sd_dhcp_client sd_dhcp_client; + +typedef void (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata); +int sd_dhcp_client_set_callback( + sd_dhcp_client *client, + sd_dhcp_client_callback_t cb, + void *userdata); + +int sd_dhcp_client_set_request_option( + sd_dhcp_client *client, + uint8_t option); +int sd_dhcp_client_set_request_address( + sd_dhcp_client *client, + const struct in_addr *last_address); +int sd_dhcp_client_set_request_broadcast( + sd_dhcp_client *client, + int broadcast); +int sd_dhcp_client_set_index( + sd_dhcp_client *client, + int interface_index); +int sd_dhcp_client_set_mac( + sd_dhcp_client *client, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type); +int sd_dhcp_client_set_client_id( + sd_dhcp_client *client, + uint8_t type, + const uint8_t *data, + size_t data_len); +int sd_dhcp_client_set_iaid_duid( + sd_dhcp_client *client, + uint32_t iaid, + uint16_t duid_type, + const void *duid, + size_t duid_len); +int sd_dhcp_client_get_client_id( + sd_dhcp_client *client, + uint8_t *type, + const uint8_t **data, + size_t *data_len); +int sd_dhcp_client_set_mtu( + sd_dhcp_client *client, + uint32_t mtu); +int sd_dhcp_client_set_hostname( + sd_dhcp_client *client, + const char *hostname); +int sd_dhcp_client_set_vendor_class_identifier( + sd_dhcp_client *client, + const char *vci); +int sd_dhcp_client_get_lease( + sd_dhcp_client *client, + sd_dhcp_lease **ret); + +int sd_dhcp_client_stop(sd_dhcp_client *client); +int sd_dhcp_client_start(sd_dhcp_client *client); + +sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client); +sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client); + +int sd_dhcp_client_new(sd_dhcp_client **ret); + +int sd_dhcp_client_attach_event( + sd_dhcp_client *client, + sd_event *event, + int64_t priority); +int sd_dhcp_client_detach_event(sd_dhcp_client *client); +sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_client, sd_dhcp_client_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-dhcp-lease.h b/src/libsystemd/include/systemd/sd-dhcp-lease.h new file mode 100644 index 0000000000..2f565ca825 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-dhcp-lease.h @@ -0,0 +1,67 @@ +#ifndef foosddhcpleasehfoo +#define foosddhcpleasehfoo + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + Copyright (C) 2014 Tom Gundersen + + 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 . +***/ + +#include +#include +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_dhcp_lease sd_dhcp_lease; +typedef struct sd_dhcp_route sd_dhcp_route; + +sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease); +sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease); + +int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime); +int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1); +int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2); +int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr); +int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr); +int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu); +int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname); +int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname); +int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path); +int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes); +int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len); +int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len); +int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone); + +int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination); +int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length); +int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-dhcp-server.h b/src/libsystemd/include/systemd/sd-dhcp-server.h new file mode 100644 index 0000000000..bbb2bb203c --- /dev/null +++ b/src/libsystemd/include/systemd/sd-dhcp-server.h @@ -0,0 +1,65 @@ +#ifndef foosddhcpserverhfoo +#define foosddhcpserverhfoo + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + Copyright (C) 2014 Tom Gundersen + + 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 . +***/ + +#include +#include + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_dhcp_server sd_dhcp_server; + +int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex); + +sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server); +sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server); + +int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int64_t priority); +int sd_dhcp_server_detach_event(sd_dhcp_server *client); +sd_event *sd_dhcp_server_get_event(sd_dhcp_server *client); + +int sd_dhcp_server_is_running(sd_dhcp_server *server); + +int sd_dhcp_server_start(sd_dhcp_server *server); +int sd_dhcp_server_stop(sd_dhcp_server *server); + +int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size); + +int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone); +int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n); +int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr dns[], unsigned n); +int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled); + +int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t); +int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t); + +int sd_dhcp_server_forcerenew(sd_dhcp_server *server); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_server, sd_dhcp_server_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-dhcp6-client.h b/src/libsystemd/include/systemd/sd-dhcp6-client.h new file mode 100644 index 0000000000..6bcd9862c9 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-dhcp6-client.h @@ -0,0 +1,135 @@ +#ifndef foosddhcp6clienthfoo +#define foosddhcp6clienthfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Intel Corporation. All rights reserved. + + 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 . +***/ + +#include +#include +#include + +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + SD_DHCP6_CLIENT_EVENT_STOP = 0, + SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE = 10, + SD_DHCP6_CLIENT_EVENT_RETRANS_MAX = 11, + SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE = 12, + SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST = 13, +}; + +enum { + SD_DHCP6_OPTION_CLIENTID = 1, + SD_DHCP6_OPTION_SERVERID = 2, + SD_DHCP6_OPTION_IA_NA = 3, + SD_DHCP6_OPTION_IA_TA = 4, + SD_DHCP6_OPTION_IAADDR = 5, + SD_DHCP6_OPTION_ORO = 6, + SD_DHCP6_OPTION_PREFERENCE = 7, + SD_DHCP6_OPTION_ELAPSED_TIME = 8, + SD_DHCP6_OPTION_RELAY_MSG = 9, + /* option code 10 is unassigned */ + SD_DHCP6_OPTION_AUTH = 11, + SD_DHCP6_OPTION_UNICAST = 12, + SD_DHCP6_OPTION_STATUS_CODE = 13, + SD_DHCP6_OPTION_RAPID_COMMIT = 14, + SD_DHCP6_OPTION_USER_CLASS = 15, + SD_DHCP6_OPTION_VENDOR_CLASS = 16, + SD_DHCP6_OPTION_VENDOR_OPTS = 17, + SD_DHCP6_OPTION_INTERFACE_ID = 18, + SD_DHCP6_OPTION_RECONF_MSG = 19, + SD_DHCP6_OPTION_RECONF_ACCEPT = 20, + + SD_DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */ + SD_DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */ + + SD_DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */ + + /* option code 35 is unassigned */ + + SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */ + + /* option codes 89-142 are unassigned */ + /* option codes 144-65535 are unassigned */ +}; + +typedef struct sd_dhcp6_client sd_dhcp6_client; + +typedef void (*sd_dhcp6_client_callback_t)(sd_dhcp6_client *client, int event, void *userdata); +int sd_dhcp6_client_set_callback( + sd_dhcp6_client *client, + sd_dhcp6_client_callback_t cb, + void *userdata); + +int sd_dhcp6_client_set_index( + sd_dhcp6_client *client, + int interface_index); +int sd_dhcp6_client_set_local_address( + sd_dhcp6_client *client, + const struct in6_addr *local_address); +int sd_dhcp6_client_set_mac( + sd_dhcp6_client *client, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type); +int sd_dhcp6_client_set_duid( + sd_dhcp6_client *client, + uint16_t duid_type, + const void *duid, + size_t duid_len); +int sd_dhcp6_client_set_iaid( + sd_dhcp6_client *client, + uint32_t iaid); +int sd_dhcp6_client_set_information_request( + sd_dhcp6_client *client, + int enabled); +int sd_dhcp6_client_get_information_request( + sd_dhcp6_client *client, + int *enabled); +int sd_dhcp6_client_set_request_option( + sd_dhcp6_client *client, + uint16_t option); + +int sd_dhcp6_client_get_lease( + sd_dhcp6_client *client, + sd_dhcp6_lease **ret); + +int sd_dhcp6_client_stop(sd_dhcp6_client *client); +int sd_dhcp6_client_start(sd_dhcp6_client *client); +int sd_dhcp6_client_is_running(sd_dhcp6_client *client); +int sd_dhcp6_client_attach_event( + sd_dhcp6_client *client, + sd_event *event, + int64_t priority); +int sd_dhcp6_client_detach_event(sd_dhcp6_client *client); +sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client); +sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client); +sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client); +int sd_dhcp6_client_new(sd_dhcp6_client **ret); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_client, sd_dhcp6_client_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-dhcp6-lease.h b/src/libsystemd/include/systemd/sd-dhcp6-lease.h new file mode 100644 index 0000000000..184fbb8e0d --- /dev/null +++ b/src/libsystemd/include/systemd/sd-dhcp6-lease.h @@ -0,0 +1,52 @@ +#ifndef foosddhcp6leasehfoo +#define foosddhcp6leasehfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. + + 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 . +***/ + +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_dhcp6_lease sd_dhcp6_lease; + +void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease); +int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, + struct in6_addr *addr, + uint32_t *lifetime_preferred, + uint32_t *lifetime_valid); + +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs); +int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, + struct in6_addr **addrs); +int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn); + +sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); +sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_lease, sd_dhcp6_lease_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-event.h b/src/libsystemd/include/systemd/sd-event.h new file mode 100644 index 0000000000..531ace1c34 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-event.h @@ -0,0 +1,142 @@ +#ifndef foosdeventhfoo +#define foosdeventhfoo + +/*** + 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 . +***/ + +#include +#include +#include +#include +#include + +#include "_sd-common.h" + +/* + Why is this better than pure epoll? + + - Supports event source prioritization + - Scales better with a large number of time events because it does not require one timerfd each + - Automatically tries to coalesce timer events system-wide + - Handles signals and child PIDs +*/ + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_event sd_event; +typedef struct sd_event_source sd_event_source; + +enum { + SD_EVENT_OFF = 0, + SD_EVENT_ON = 1, + SD_EVENT_ONESHOT = -1 +}; + +enum { + SD_EVENT_INITIAL, + SD_EVENT_ARMED, + SD_EVENT_PENDING, + SD_EVENT_RUNNING, + SD_EVENT_EXITING, + SD_EVENT_FINISHED, + SD_EVENT_PREPARING +}; + +enum { + /* And everything in-between and outside is good too */ + SD_EVENT_PRIORITY_IMPORTANT = -100, + SD_EVENT_PRIORITY_NORMAL = 0, + SD_EVENT_PRIORITY_IDLE = 100 +}; + +typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata); +typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata); +typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata); +typedef int (*sd_event_signal_handler_t)(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata); +#if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED +typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, void *userdata); +#else +typedef void* sd_event_child_handler_t; +#endif + +int sd_event_default(sd_event **e); + +int sd_event_new(sd_event **e); +sd_event* sd_event_ref(sd_event *e); +sd_event* sd_event_unref(sd_event *e); + +int sd_event_add_io(sd_event *e, sd_event_source **s, int fd, uint32_t events, sd_event_io_handler_t callback, void *userdata); +int sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata); +int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_signal_handler_t callback, void *userdata); +int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata); +int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); +int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); +int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); + +int sd_event_prepare(sd_event *e); +int sd_event_wait(sd_event *e, uint64_t usec); +int sd_event_dispatch(sd_event *e); +int sd_event_run(sd_event *e, uint64_t usec); +int sd_event_loop(sd_event *e); +int sd_event_exit(sd_event *e, int code); + +int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec); + +int sd_event_get_fd(sd_event *e); +int sd_event_get_state(sd_event *e); +int sd_event_get_tid(sd_event *e, pid_t *tid); +int sd_event_get_exit_code(sd_event *e, int *code); +int sd_event_set_watchdog(sd_event *e, int b); +int sd_event_get_watchdog(sd_event *e); + +sd_event_source* sd_event_source_ref(sd_event_source *s); +sd_event_source* sd_event_source_unref(sd_event_source *s); + +sd_event *sd_event_source_get_event(sd_event_source *s); +void* sd_event_source_get_userdata(sd_event_source *s); +void* sd_event_source_set_userdata(sd_event_source *s, void *userdata); + +int sd_event_source_set_description(sd_event_source *s, const char *description); +int sd_event_source_get_description(sd_event_source *s, const char **description); +int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback); +int sd_event_source_get_pending(sd_event_source *s); +int sd_event_source_get_priority(sd_event_source *s, int64_t *priority); +int sd_event_source_set_priority(sd_event_source *s, int64_t priority); +int sd_event_source_get_enabled(sd_event_source *s, int *enabled); +int sd_event_source_set_enabled(sd_event_source *s, int enabled); +int sd_event_source_get_io_fd(sd_event_source *s); +int sd_event_source_set_io_fd(sd_event_source *s, int fd); +int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events); +int sd_event_source_set_io_events(sd_event_source *s, uint32_t events); +int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents); +int sd_event_source_get_time(sd_event_source *s, uint64_t *usec); +int sd_event_source_set_time(sd_event_source *s, uint64_t usec); +int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec); +int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec); +int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock); +int sd_event_source_get_signal(sd_event_source *s); +int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid); + +/* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event_source, sd_event_source_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-hwdb.h b/src/libsystemd/include/systemd/sd-hwdb.h new file mode 100644 index 0000000000..7105920492 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-hwdb.h @@ -0,0 +1,49 @@ +#ifndef foosdhwdbhfoo +#define foosdhwdbhfoo + +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + Copyright 2014 Tom Gundersen + + 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 . +***/ + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_hwdb sd_hwdb; + +sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb); +sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb); + +int sd_hwdb_new(sd_hwdb **ret); + +int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **value); + +int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias); +int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value); + +/* the inverse condition avoids ambiguity of dangling 'else' after the macro */ +#define SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) \ + if (sd_hwdb_seek(hwdb, modalias) < 0) { } \ + else while (sd_hwdb_enumerate(hwdb, &(key), &(value)) > 0) + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_hwdb, sd_hwdb_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-id128.h b/src/libsystemd/include/systemd/sd-id128.h new file mode 100644 index 0000000000..4dff0b9b81 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-id128.h @@ -0,0 +1,115 @@ +#ifndef foosdid128hfoo +#define foosdid128hfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* 128-bit ID APIs. See sd-id128(3) for more information. */ + +typedef union sd_id128 sd_id128_t; + +union sd_id128 { + uint8_t bytes[16]; + uint64_t qwords[2]; +}; + +#define SD_ID128_STRING_MAX 33 + +char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]); + +int sd_id128_from_string(const char *s, sd_id128_t *ret); + +int sd_id128_randomize(sd_id128_t *ret); + +int sd_id128_get_machine(sd_id128_t *ret); + +int sd_id128_get_boot(sd_id128_t *ret); + +#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ + ((const sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ + 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}) + +#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ + { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ + 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }} + +/* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16 + * times. It is hence not a good idea to call this macro with an + * expensive function as parameter or an expression with side + * effects */ + +#define SD_ID128_FORMAT_STR "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" +#define SD_ID128_FORMAT_VAL(x) (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] + +#define SD_ID128_CONST_STR(x) \ + ((const char[SD_ID128_STRING_MAX]) { \ + ((x).bytes[0] >> 4) >= 10 ? 'a' + ((x).bytes[0] >> 4) - 10 : '0' + ((x).bytes[0] >> 4), \ + ((x).bytes[0] & 15) >= 10 ? 'a' + ((x).bytes[0] & 15) - 10 : '0' + ((x).bytes[0] & 15), \ + ((x).bytes[1] >> 4) >= 10 ? 'a' + ((x).bytes[1] >> 4) - 10 : '0' + ((x).bytes[1] >> 4), \ + ((x).bytes[1] & 15) >= 10 ? 'a' + ((x).bytes[1] & 15) - 10 : '0' + ((x).bytes[1] & 15), \ + ((x).bytes[2] >> 4) >= 10 ? 'a' + ((x).bytes[2] >> 4) - 10 : '0' + ((x).bytes[2] >> 4), \ + ((x).bytes[2] & 15) >= 10 ? 'a' + ((x).bytes[2] & 15) - 10 : '0' + ((x).bytes[2] & 15), \ + ((x).bytes[3] >> 4) >= 10 ? 'a' + ((x).bytes[3] >> 4) - 10 : '0' + ((x).bytes[3] >> 4), \ + ((x).bytes[3] & 15) >= 10 ? 'a' + ((x).bytes[3] & 15) - 10 : '0' + ((x).bytes[3] & 15), \ + ((x).bytes[4] >> 4) >= 10 ? 'a' + ((x).bytes[4] >> 4) - 10 : '0' + ((x).bytes[4] >> 4), \ + ((x).bytes[4] & 15) >= 10 ? 'a' + ((x).bytes[4] & 15) - 10 : '0' + ((x).bytes[4] & 15), \ + ((x).bytes[5] >> 4) >= 10 ? 'a' + ((x).bytes[5] >> 4) - 10 : '0' + ((x).bytes[5] >> 4), \ + ((x).bytes[5] & 15) >= 10 ? 'a' + ((x).bytes[5] & 15) - 10 : '0' + ((x).bytes[5] & 15), \ + ((x).bytes[6] >> 4) >= 10 ? 'a' + ((x).bytes[6] >> 4) - 10 : '0' + ((x).bytes[6] >> 4), \ + ((x).bytes[6] & 15) >= 10 ? 'a' + ((x).bytes[6] & 15) - 10 : '0' + ((x).bytes[6] & 15), \ + ((x).bytes[7] >> 4) >= 10 ? 'a' + ((x).bytes[7] >> 4) - 10 : '0' + ((x).bytes[7] >> 4), \ + ((x).bytes[7] & 15) >= 10 ? 'a' + ((x).bytes[7] & 15) - 10 : '0' + ((x).bytes[7] & 15), \ + ((x).bytes[8] >> 4) >= 10 ? 'a' + ((x).bytes[8] >> 4) - 10 : '0' + ((x).bytes[8] >> 4), \ + ((x).bytes[8] & 15) >= 10 ? 'a' + ((x).bytes[8] & 15) - 10 : '0' + ((x).bytes[8] & 15), \ + ((x).bytes[9] >> 4) >= 10 ? 'a' + ((x).bytes[9] >> 4) - 10 : '0' + ((x).bytes[9] >> 4), \ + ((x).bytes[9] & 15) >= 10 ? 'a' + ((x).bytes[9] & 15) - 10 : '0' + ((x).bytes[9] & 15), \ + ((x).bytes[10] >> 4) >= 10 ? 'a' + ((x).bytes[10] >> 4) - 10 : '0' + ((x).bytes[10] >> 4), \ + ((x).bytes[10] & 15) >= 10 ? 'a' + ((x).bytes[10] & 15) - 10 : '0' + ((x).bytes[10] & 15), \ + ((x).bytes[11] >> 4) >= 10 ? 'a' + ((x).bytes[11] >> 4) - 10 : '0' + ((x).bytes[11] >> 4), \ + ((x).bytes[11] & 15) >= 10 ? 'a' + ((x).bytes[11] & 15) - 10 : '0' + ((x).bytes[11] & 15), \ + ((x).bytes[12] >> 4) >= 10 ? 'a' + ((x).bytes[12] >> 4) - 10 : '0' + ((x).bytes[12] >> 4), \ + ((x).bytes[12] & 15) >= 10 ? 'a' + ((x).bytes[12] & 15) - 10 : '0' + ((x).bytes[12] & 15), \ + ((x).bytes[13] >> 4) >= 10 ? 'a' + ((x).bytes[13] >> 4) - 10 : '0' + ((x).bytes[13] >> 4), \ + ((x).bytes[13] & 15) >= 10 ? 'a' + ((x).bytes[13] & 15) - 10 : '0' + ((x).bytes[13] & 15), \ + ((x).bytes[14] >> 4) >= 10 ? 'a' + ((x).bytes[14] >> 4) - 10 : '0' + ((x).bytes[14] >> 4), \ + ((x).bytes[14] & 15) >= 10 ? 'a' + ((x).bytes[14] & 15) - 10 : '0' + ((x).bytes[14] & 15), \ + ((x).bytes[15] >> 4) >= 10 ? 'a' + ((x).bytes[15] >> 4) - 10 : '0' + ((x).bytes[15] >> 4), \ + ((x).bytes[15] & 15) >= 10 ? 'a' + ((x).bytes[15] & 15) - 10 : '0' + ((x).bytes[15] & 15), \ + 0 }) + +_sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) { + return memcmp(&a, &b, 16) == 0; +} + +_sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) { + return a.qwords[0] == 0 && a.qwords[1] == 0; +} + +#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }}) + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-ipv4acd.h b/src/libsystemd/include/systemd/sd-ipv4acd.h new file mode 100644 index 0000000000..93db7a4a6c --- /dev/null +++ b/src/libsystemd/include/systemd/sd-ipv4acd.h @@ -0,0 +1,60 @@ +#ifndef foosdipv4acdfoo +#define foosdipv4acdfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 2015 Tom Gundersen + + 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 . +***/ + +#include +#include + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + SD_IPV4ACD_EVENT_STOP = 0, + SD_IPV4ACD_EVENT_BIND = 1, + SD_IPV4ACD_EVENT_CONFLICT = 2, +}; + +typedef struct sd_ipv4acd sd_ipv4acd; +typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *ll, int event, void *userdata); + +int sd_ipv4acd_detach_event(sd_ipv4acd *ll); +int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority); +int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address); +int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata); +int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr); +int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index); +int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address); +int sd_ipv4acd_is_running(sd_ipv4acd *ll); +int sd_ipv4acd_start(sd_ipv4acd *ll); +int sd_ipv4acd_stop(sd_ipv4acd *ll); +sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll); +sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll); +int sd_ipv4acd_new(sd_ipv4acd **ret); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4acd, sd_ipv4acd_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-ipv4ll.h b/src/libsystemd/include/systemd/sd-ipv4ll.h new file mode 100644 index 0000000000..9167623167 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-ipv4ll.h @@ -0,0 +1,60 @@ +#ifndef foosdipv4llfoo +#define foosdipv4llfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. All rights reserved. + + 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 . +***/ + +#include +#include + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + SD_IPV4LL_EVENT_STOP = 0, + SD_IPV4LL_EVENT_BIND = 1, + SD_IPV4LL_EVENT_CONFLICT = 2, +}; + +typedef struct sd_ipv4ll sd_ipv4ll; +typedef void (*sd_ipv4ll_callback_t)(sd_ipv4ll *ll, int event, void *userdata); + +int sd_ipv4ll_detach_event(sd_ipv4ll *ll); +int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority); +int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address); +int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata); +int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr); +int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index); +int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address); +int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed); +int sd_ipv4ll_is_running(sd_ipv4ll *ll); +int sd_ipv4ll_start(sd_ipv4ll *ll); +int sd_ipv4ll_stop(sd_ipv4ll *ll); +sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll); +sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll); +int sd_ipv4ll_new (sd_ipv4ll **ret); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4ll, sd_ipv4ll_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-journal.h b/src/libsystemd/include/systemd/sd-journal.h new file mode 100644 index 0000000000..3e61feb81f --- /dev/null +++ b/src/libsystemd/include/systemd/sd-journal.h @@ -0,0 +1,175 @@ +#ifndef foosdjournalhfoo +#define foosdjournalhfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "_sd-common.h" + +/* Journal APIs. See sd-journal(3) for more information. */ + +_SD_BEGIN_DECLARATIONS; + +/* Write to daemon */ +int sd_journal_print(int priority, const char *format, ...) _sd_printf_(2, 3); +int sd_journal_printv(int priority, const char *format, va_list ap) _sd_printf_(2, 0); +int sd_journal_send(const char *format, ...) _sd_printf_(1, 0) _sd_sentinel_; +int sd_journal_sendv(const struct iovec *iov, int n); +int sd_journal_perror(const char *message); + +/* Used by the macros below. You probably don't want to call this directly. */ +int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) _sd_printf_(5, 6); +int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) _sd_printf_(5, 0); +int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) _sd_printf_(4, 0) _sd_sentinel_; +int sd_journal_sendv_with_location(const char *file, const char *line, const char *func, const struct iovec *iov, int n); +int sd_journal_perror_with_location(const char *file, const char *line, const char *func, const char *message); + +/* implicitly add code location to messages sent, if this is enabled */ +#ifndef SD_JOURNAL_SUPPRESS_LOCATION + +#define sd_journal_print(priority, ...) sd_journal_print_with_location(priority, "CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, __VA_ARGS__) +#define sd_journal_printv(priority, format, ap) sd_journal_printv_with_location(priority, "CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, format, ap) +#define sd_journal_send(...) sd_journal_send_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, __VA_ARGS__) +#define sd_journal_sendv(iovec, n) sd_journal_sendv_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, iovec, n) +#define sd_journal_perror(message) sd_journal_perror_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, message) + +#endif + +int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix); + +/* Browse journal stream */ + +typedef struct sd_journal sd_journal; + +/* Open flags */ +enum { + SD_JOURNAL_LOCAL_ONLY = 1 << 0, + SD_JOURNAL_RUNTIME_ONLY = 1 << 1, + SD_JOURNAL_SYSTEM = 1 << 2, + SD_JOURNAL_CURRENT_USER = 1 << 3, + SD_JOURNAL_OS_ROOT = 1 << 4, + + SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM /* deprecated name */ +}; + +/* Wakeup event types */ +enum { + SD_JOURNAL_NOP, + SD_JOURNAL_APPEND, + SD_JOURNAL_INVALIDATE +}; + +int sd_journal_open(sd_journal **ret, int flags); +int sd_journal_open_directory(sd_journal **ret, const char *path, int flags); +int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags); +int sd_journal_open_files(sd_journal **ret, const char **paths, int flags); +int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags); +int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); /* deprecated */ +void sd_journal_close(sd_journal *j); + +int sd_journal_previous(sd_journal *j); +int sd_journal_next(sd_journal *j); + +int sd_journal_previous_skip(sd_journal *j, uint64_t skip); +int sd_journal_next_skip(sd_journal *j, uint64_t skip); + +int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret); +int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id); + +int sd_journal_set_data_threshold(sd_journal *j, size_t sz); +int sd_journal_get_data_threshold(sd_journal *j, size_t *sz); + +int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *l); +int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *l); +void sd_journal_restart_data(sd_journal *j); + +int sd_journal_add_match(sd_journal *j, const void *data, size_t size); +int sd_journal_add_disjunction(sd_journal *j); +int sd_journal_add_conjunction(sd_journal *j); +void sd_journal_flush_matches(sd_journal *j); + +int sd_journal_seek_head(sd_journal *j); +int sd_journal_seek_tail(sd_journal *j); +int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec); +int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec); +int sd_journal_seek_cursor(sd_journal *j, const char *cursor); + +int sd_journal_get_cursor(sd_journal *j, char **cursor); +int sd_journal_test_cursor(sd_journal *j, const char *cursor); + +int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to); +int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, const sd_id128_t boot_id, uint64_t *from, uint64_t *to); + +int sd_journal_get_usage(sd_journal *j, uint64_t *bytes); + +int sd_journal_query_unique(sd_journal *j, const char *field); +int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l); +void sd_journal_restart_unique(sd_journal *j); + +int sd_journal_enumerate_fields(sd_journal *j, const char **field); +void sd_journal_restart_fields(sd_journal *j); + +int sd_journal_get_fd(sd_journal *j); +int sd_journal_get_events(sd_journal *j); +int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec); +int sd_journal_process(sd_journal *j); +int sd_journal_wait(sd_journal *j, uint64_t timeout_usec); +int sd_journal_reliable_fd(sd_journal *j); + +int sd_journal_get_catalog(sd_journal *j, char **text); +int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **text); + +int sd_journal_has_runtime_files(sd_journal *j); +int sd_journal_has_persistent_files(sd_journal *j); + +/* The inverse condition avoids ambiguity of dangling 'else' after the macro */ +#define SD_JOURNAL_FOREACH(j) \ + if (sd_journal_seek_head(j) < 0) { } \ + else while (sd_journal_next(j) > 0) + +/* The inverse condition avoids ambiguity of dangling 'else' after the macro */ +#define SD_JOURNAL_FOREACH_BACKWARDS(j) \ + if (sd_journal_seek_tail(j) < 0) { } \ + else while (sd_journal_previous(j) > 0) + +/* Iterate through the data fields of the current journal entry */ +#define SD_JOURNAL_FOREACH_DATA(j, data, l) \ + for (sd_journal_restart_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; ) + +/* Iterate through the all known values of a specific field */ +#define SD_JOURNAL_FOREACH_UNIQUE(j, data, l) \ + for (sd_journal_restart_unique(j); sd_journal_enumerate_unique((j), &(data), &(l)) > 0; ) + +/* Iterate through all known field names */ +#define SD_JOURNAL_FOREACH_FIELD(j, field) \ + for (sd_journal_restart_fields(j); sd_journal_enumerate_fields((j), &(field)) > 0; ) + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_journal, sd_journal_close); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-lldp.h b/src/libsystemd/include/systemd/sd-lldp.h new file mode 100644 index 0000000000..391e7c2a2e --- /dev/null +++ b/src/libsystemd/include/systemd/sd-lldp.h @@ -0,0 +1,177 @@ +#ifndef foosdlldphfoo +#define foosdlldphfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + Copyright (C) 2014 Susant Sahani + + 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 . +***/ + +#include +#include + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_lldp sd_lldp; +typedef struct sd_lldp_neighbor sd_lldp_neighbor; + +/* IEEE 802.3AB Clause 9: TLV Types */ +enum { + SD_LLDP_TYPE_END = 0, + SD_LLDP_TYPE_CHASSIS_ID = 1, + SD_LLDP_TYPE_PORT_ID = 2, + SD_LLDP_TYPE_TTL = 3, + SD_LLDP_TYPE_PORT_DESCRIPTION = 4, + SD_LLDP_TYPE_SYSTEM_NAME = 5, + SD_LLDP_TYPE_SYSTEM_DESCRIPTION = 6, + SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7, + SD_LLDP_TYPE_MGMT_ADDRESS = 8, + SD_LLDP_TYPE_PRIVATE = 127, +}; + +/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */ +enum { + SD_LLDP_CHASSIS_SUBTYPE_RESERVED = 0, + SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1, + SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2, + SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT = 3, + SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS = 4, + SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5, + SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6, + SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7, +}; + +/* IEEE 802.3AB Clause 9.5.3: Port subtype */ +enum { + SD_LLDP_PORT_SUBTYPE_RESERVED = 0, + SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1, + SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2, + SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3, + SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4, + SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5, + SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6, + SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7, +}; + +enum { + SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0, + SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1, + SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2, + SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3, + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4, + SD_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5, + SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6, + SD_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7, + SD_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8, + SD_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9, + SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10, +}; + +#define SD_LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1) + +#define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \ + ((uint16_t) \ + (SD_LLDP_SYSTEM_CAPABILITIES_REPEATER| \ + SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE| \ + SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP| \ + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER| \ + SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS| \ + SD_LLDP_SYSTEM_CAPABILITIES_CVLAN| \ + SD_LLDP_SYSTEM_CAPABILITIES_SVLAN| \ + SD_LLDP_SYSTEM_CAPABILITIES_TPMR)) + +#define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 } +#define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } + +enum { + SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1, + SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2, + SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3, + SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4, + SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5, + SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6, + SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7, +}; + +typedef enum sd_lldp_event { + SD_LLDP_EVENT_ADDED = 'a', + SD_LLDP_EVENT_REMOVED = 'r', + SD_LLDP_EVENT_UPDATED = 'u', + SD_LLDP_EVENT_REFRESHED = 'f', +} sd_lldp_event; + +typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata); + +int sd_lldp_new(sd_lldp **ret, int ifindex); +sd_lldp* sd_lldp_unref(sd_lldp *lldp); + +int sd_lldp_start(sd_lldp *lldp); +int sd_lldp_stop(sd_lldp *lldp); + +int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority); +int sd_lldp_detach_event(sd_lldp *lldp); + +int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata); + +/* Controls how much and what to store in the neighbors database */ +int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n); +int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask); +int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *address); + +int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***neighbors); + +int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size); +sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n); +sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n); + +/* Access to LLDP frame metadata */ +int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address); +int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address); +int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); + +/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */ +int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); +int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); +int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret); +int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret); +int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret); + +/* Low-level, iterative TLV access. This is for evertyhing else, it iteratively goes through all available TLVs + * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */ +int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n); +int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n); +int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type); +int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type); +int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype); +int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype); +int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-login.h b/src/libsystemd/include/systemd/sd-login.h new file mode 100644 index 0000000000..e3ecbd8378 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-login.h @@ -0,0 +1,245 @@ +#ifndef foosdloginhfoo +#define foosdloginhfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include + +#include "_sd-common.h" + +/* + * A few points: + * + * Instead of returning an empty string array or empty uid array, we + * may return NULL. + * + * Free the data the library returns with libc free(). String arrays + * are NULL terminated, and you need to free the array itself, in + * addition to the strings contained. + * + * We return error codes as negative errno, kernel-style. On success, we + * return 0 or positive. + * + * These functions access data in /proc, /sys/fs/cgroup, and /run. All + * of these are virtual file systems; therefore, accesses are + * relatively cheap. + * + * See sd-login(3) for more information. + */ + +_SD_BEGIN_DECLARATIONS; + +/* Get session from PID. Note that 'shared' processes of a user are + * not attached to a session, but only attached to a user. This will + * return an error for system processes and 'shared' processes of a + * user. */ +int sd_pid_get_session(pid_t pid, char **session); + +/* Get UID of the owner of the session of the PID (or in case the + * process is a 'shared' user process, the UID of that user is + * returned). This will not return the UID of the process, but rather + * the UID of the owner of the cgroup that the process is in. This will + * return an error for system processes. */ +int sd_pid_get_owner_uid(pid_t pid, uid_t *uid); + +/* Get systemd non-slice unit (i.e. service) name from PID, for system + * services. This will return an error for non-service processes. */ +int sd_pid_get_unit(pid_t pid, char **unit); + +/* Get systemd non-slice unit (i.e. service) name from PID, for user + * services. This will return an error for non-user-service + * processes. */ +int sd_pid_get_user_unit(pid_t pid, char **unit); + +/* Get slice name from PID. */ +int sd_pid_get_slice(pid_t pid, char **slice); + +/* Get user slice name from PID. */ +int sd_pid_get_user_slice(pid_t pid, char **slice); + +/* Get machine name from PID, for processes assigned to a VM or + * container. This will return an error for non-machine processes. */ +int sd_pid_get_machine_name(pid_t pid, char **machine); + +/* Get the control group from a PID, relative to the root of the + * hierarchy. */ +int sd_pid_get_cgroup(pid_t pid, char **cgroup); + +/* Similar to sd_pid_get_session(), but retrieves data about the peer + * of a connected AF_UNIX socket */ +int sd_peer_get_session(int fd, char **session); + +/* Similar to sd_pid_get_owner_uid(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_owner_uid(int fd, uid_t *uid); + +/* Similar to sd_pid_get_unit(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_unit(int fd, char **unit); + +/* Similar to sd_pid_get_user_unit(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_user_unit(int fd, char **unit); + +/* Similar to sd_pid_get_slice(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_slice(int fd, char **slice); + +/* Similar to sd_pid_get_user_slice(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_user_slice(int fd, char **slice); + +/* Similar to sd_pid_get_machine_name(), but retrieves data about the + * peer of a connected AF_UNIX socket */ +int sd_peer_get_machine_name(int fd, char **machine); + +/* Similar to sd_pid_get_cgroup(), but retrieves data about the peer + * of a connected AF_UNIX socket. */ +int sd_peer_get_cgroup(pid_t pid, char **cgroup); + +/* Get state from UID. Possible states: offline, lingering, online, active, closing */ +int sd_uid_get_state(uid_t uid, char **state); + +/* Return primary session of user, if there is any */ +int sd_uid_get_display(uid_t uid, char **session); + +/* Return 1 if UID has session on seat. If require_active is true, this will + * look for active sessions only. */ +int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat); + +/* Return sessions of user. If require_active is true, this will look for + * active sessions only. Returns the number of sessions. + * If sessions is NULL, this will just return the number of sessions. */ +int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions); + +/* Return seats of user is on. If require_active is true, this will look for + * active seats only. Returns the number of seats. + * If seats is NULL, this will just return the number of seats. */ +int sd_uid_get_seats(uid_t uid, int require_active, char ***seats); + +/* Return 1 if the session is active. */ +int sd_session_is_active(const char *session); + +/* Return 1 if the session is remote. */ +int sd_session_is_remote(const char *session); + +/* Get state from session. Possible states: online, active, closing. + * This function is a more generic version of sd_session_is_active(). */ +int sd_session_get_state(const char *session, char **state); + +/* Determine user ID of session */ +int sd_session_get_uid(const char *session, uid_t *uid); + +/* Determine seat of session */ +int sd_session_get_seat(const char *session, char **seat); + +/* Determine the (PAM) service name this session was registered by. */ +int sd_session_get_service(const char *session, char **service); + +/* Determine the type of this session, i.e. one of "tty", "x11", "wayland", "mir" or "unspecified". */ +int sd_session_get_type(const char *session, char **type); + +/* Determine the class of this session, i.e. one of "user", "greeter" or "lock-screen". */ +int sd_session_get_class(const char *session, char **clazz); + +/* Determine the desktop brand of this session, i.e. something like "GNOME", "KDE" or "systemd-console". */ +int sd_session_get_desktop(const char *session, char **desktop); + +/* Determine the X11 display of this session. */ +int sd_session_get_display(const char *session, char **display); + +/* Determine the remote host of this session. */ +int sd_session_get_remote_host(const char *session, char **remote_host); + +/* Determine the remote user of this session (if provided by PAM). */ +int sd_session_get_remote_user(const char *session, char **remote_user); + +/* Determine the TTY of this session. */ +int sd_session_get_tty(const char *session, char **display); + +/* Determine the VT number of this session. */ +int sd_session_get_vt(const char *session, unsigned *vtnr); + +/* Return active session and user of seat */ +int sd_seat_get_active(const char *seat, char **session, uid_t *uid); + +/* Return sessions and users on seat. Returns number of sessions. + * If sessions is NULL, this returns only the number of sessions. */ +int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uid, unsigned *n_uids); + +/* Return whether the seat is multi-session capable */ +int sd_seat_can_multi_session(const char *seat); + +/* Return whether the seat is TTY capable, i.e. suitable for showing console UIs */ +int sd_seat_can_tty(const char *seat); + +/* Return whether the seat is graphics capable, i.e. suitable for showing graphical UIs */ +int sd_seat_can_graphical(const char *seat); + +/* Return the class of machine */ +int sd_machine_get_class(const char *machine, char **clazz); + +/* Return the list if host-side network interface indices of a machine */ +int sd_machine_get_ifindices(const char *machine, int **ifindices); + +/* Get all seats, store in *seats. Returns the number of seats. If + * seats is NULL, this only returns the number of seats. */ +int sd_get_seats(char ***seats); + +/* Get all sessions, store in *sessions. Returns the number of + * sessions. If sessions is NULL, this only returns the number of sessions. */ +int sd_get_sessions(char ***sessions); + +/* Get all logged in users, store in *users. Returns the number of + * users. If users is NULL, this only returns the number of users. */ +int sd_get_uids(uid_t **users); + +/* Get all running virtual machines/containers */ +int sd_get_machine_names(char ***machines); + +/* Monitor object */ +typedef struct sd_login_monitor sd_login_monitor; + +/* Create a new monitor. Category must be NULL, "seat", "session", + * "uid", or "machine" to get monitor events for the specific category + * (or all). */ +int sd_login_monitor_new(const char *category, sd_login_monitor** ret); + +/* Destroys the passed monitor. Returns NULL. */ +sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m); + +/* Flushes the monitor */ +int sd_login_monitor_flush(sd_login_monitor *m); + +/* Get FD from monitor */ +int sd_login_monitor_get_fd(sd_login_monitor *m); + +/* Get poll() mask to monitor */ +int sd_login_monitor_get_events(sd_login_monitor *m); + +/* Get timeout for poll(), as usec value relative to CLOCK_MONOTONIC's epoch */ +int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_login_monitor, sd_login_monitor_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-messages.h b/src/libsystemd/include/systemd/sd-messages.h new file mode 100644 index 0000000000..1865e0492f --- /dev/null +++ b/src/libsystemd/include/systemd/sd-messages.h @@ -0,0 +1,91 @@ +#ifndef foosdmessageshfoo +#define foosdmessageshfoo + +/*** + This file is part of systemd. + + Copyright 2012 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 . +***/ + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* Hey! If you add a new message here, you *must* also update the + * message catalog with an appropriate explanation */ + +/* And if you add a new ID here, make sure to generate a random one + * with journalctl --new-id128. Do not use any other IDs, and do not + * count them up manually. */ + +#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b) +#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) +#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) +#define SD_MESSAGE_JOURNAL_MISSED SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06) +#define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6) + +#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) + +#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66) +#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a) +#define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b) +#define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5) +#define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2) +#define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58) + +#define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27) +#define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90) + +#define SD_MESSAGE_STARTUP_FINISHED SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff) + +#define SD_MESSAGE_SLEEP_START SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28) +#define SD_MESSAGE_SLEEP_STOP SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14) + +#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40) + +#define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5) +#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf) +#define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f) +#define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86) +#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d) +#define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25) +#define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54) + +#define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) + +#define SD_MESSAGE_FORWARD_SYSLOG_MISSED SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e) + +#define SD_MESSAGE_OVERMOUNTING SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7) + +#define SD_MESSAGE_LID_OPENED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f) +#define SD_MESSAGE_LID_CLOSED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70) +#define SD_MESSAGE_SYSTEM_DOCKED SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff) +#define SD_MESSAGE_SYSTEM_UNDOCKED SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53) +#define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71) +#define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72) +#define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73) + +#define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01) + +#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d) +#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65) +#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-ndisc.h b/src/libsystemd/include/systemd/sd-ndisc.h new file mode 100644 index 0000000000..c77a435d17 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-ndisc.h @@ -0,0 +1,84 @@ +#ifndef foosdndiscfoo +#define foosdndiscfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Intel Corporation. All rights reserved. + + 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 . +***/ + +#include +#include + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + SD_NDISC_EVENT_STOP = 0, + SD_NDISC_EVENT_TIMEOUT = 1, +}; + +typedef struct sd_ndisc sd_ndisc; + +typedef void(*sd_ndisc_router_callback_t)(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata); +typedef void(*sd_ndisc_prefix_onlink_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, + unsigned lifetime, void *userdata); +typedef void(*sd_ndisc_prefix_autonomous_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, + unsigned lifetime_prefered, unsigned lifetime_valid, void *userdata); +typedef void(*sd_ndisc_callback_t)(sd_ndisc *nd, int event, void *userdata); + +int sd_ndisc_set_callback(sd_ndisc *nd, + sd_ndisc_router_callback_t rcb, + sd_ndisc_prefix_onlink_callback_t plcb, + sd_ndisc_prefix_autonomous_callback_t pacb, + sd_ndisc_callback_t cb, + void *userdata); +int sd_ndisc_set_index(sd_ndisc *nd, int interface_index); +int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr); + +int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority); +int sd_ndisc_detach_event(sd_ndisc *nd); +sd_event *sd_ndisc_get_event(sd_ndisc *nd); + +sd_ndisc *sd_ndisc_ref(sd_ndisc *nd); +sd_ndisc *sd_ndisc_unref(sd_ndisc *nd); +int sd_ndisc_new(sd_ndisc **ret); + +int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu); + +int sd_ndisc_stop(sd_ndisc *nd); +int sd_ndisc_router_discovery_start(sd_ndisc *nd); + +#define SD_NDISC_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" + +#define SD_NDISC_ADDRESS_FORMAT_VAL(address) \ + be16toh((address).s6_addr16[0]), \ + be16toh((address).s6_addr16[1]), \ + be16toh((address).s6_addr16[2]), \ + be16toh((address).s6_addr16[3]), \ + be16toh((address).s6_addr16[4]), \ + be16toh((address).s6_addr16[5]), \ + be16toh((address).s6_addr16[6]), \ + be16toh((address).s6_addr16[7]) + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-netlink.h b/src/libsystemd/include/systemd/sd-netlink.h new file mode 100644 index 0000000000..c4cefe4e30 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-netlink.h @@ -0,0 +1,163 @@ +#ifndef foosdnetlinkhfoo +#define foosdnetlinkhfoo + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_netlink sd_netlink; +typedef struct sd_netlink_message sd_netlink_message; + +/* callback */ + +typedef int (*sd_netlink_message_handler_t)(sd_netlink *nl, sd_netlink_message *m, void *userdata); + +/* bus */ +int sd_netlink_new_from_netlink(sd_netlink **nl, int fd); +int sd_netlink_open(sd_netlink **nl); +int sd_netlink_open_fd(sd_netlink **nl, int fd); +int sd_netlink_inc_rcvbuf(const sd_netlink *const rtnl, const int size); + +sd_netlink *sd_netlink_ref(sd_netlink *nl); +sd_netlink *sd_netlink_unref(sd_netlink *nl); + +int sd_netlink_send(sd_netlink *nl, sd_netlink_message *message, uint32_t *serial); +int sd_netlink_call_async(sd_netlink *nl, sd_netlink_message *message, + sd_netlink_message_handler_t callback, + void *userdata, uint64_t usec, uint32_t *serial); +int sd_netlink_call_async_cancel(sd_netlink *nl, uint32_t serial); +int sd_netlink_call(sd_netlink *nl, sd_netlink_message *message, uint64_t timeout, + sd_netlink_message **reply); + +int sd_netlink_get_events(sd_netlink *nl); +int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout); +int sd_netlink_process(sd_netlink *nl, sd_netlink_message **ret); +int sd_netlink_wait(sd_netlink *nl, uint64_t timeout); + +int sd_netlink_add_match(sd_netlink *nl, uint16_t match, sd_netlink_message_handler_t c, void *userdata); +int sd_netlink_remove_match(sd_netlink *nl, uint16_t match, sd_netlink_message_handler_t c, void *userdata); + +int sd_netlink_attach_event(sd_netlink *nl, sd_event *e, int64_t priority); +int sd_netlink_detach_event(sd_netlink *nl); + +int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data); +int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type); +int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data); +int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data); +int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data); +int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len); +int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data); +int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data); +int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data); +int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info); + +int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type); +int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key); +int sd_netlink_message_close_container(sd_netlink_message *m); + +int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data); +int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data); +int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data); +int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data); +int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data); +int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info); +int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data); +int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data); +int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type); +int sd_netlink_message_exit_container(sd_netlink_message *m); + +int sd_netlink_message_rewind(sd_netlink_message *m); + +sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m); + +sd_netlink_message *sd_netlink_message_ref(sd_netlink_message *m); +sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m); + +int sd_netlink_message_request_dump(sd_netlink_message *m, int dump); +int sd_netlink_message_is_error(sd_netlink_message *m); +int sd_netlink_message_get_errno(sd_netlink_message *m); +int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type); +int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags); +int sd_netlink_message_is_broadcast(sd_netlink_message *m); + +/* rtnl */ + +int sd_rtnl_message_new_link(sd_netlink *nl, sd_netlink_message **ret, uint16_t msg_type, int index); +int sd_rtnl_message_new_addr_update(sd_netlink *nl, sd_netlink_message **ret, int index, int family); +int sd_rtnl_message_new_addr(sd_netlink *nl, sd_netlink_message **ret, uint16_t msg_type, int index, int family); +int sd_rtnl_message_new_route(sd_netlink *nl, sd_netlink_message **ret, uint16_t nlmsg_type, int rtm_family, unsigned char rtm_protocol); +int sd_rtnl_message_new_neigh(sd_netlink *nl, sd_netlink_message **ret, uint16_t msg_type, int index, int nda_family); + +int sd_rtnl_message_get_family(sd_netlink_message *m, int *family); + +int sd_rtnl_message_addr_set_prefixlen(sd_netlink_message *m, unsigned char prefixlen); +int sd_rtnl_message_addr_set_scope(sd_netlink_message *m, unsigned char scope); +int sd_rtnl_message_addr_set_flags(sd_netlink_message *m, unsigned char flags); +int sd_rtnl_message_addr_get_family(sd_netlink_message *m, int *family); +int sd_rtnl_message_addr_get_prefixlen(sd_netlink_message *m, unsigned char *prefixlen); +int sd_rtnl_message_addr_get_scope(sd_netlink_message *m, unsigned char *scope); +int sd_rtnl_message_addr_get_flags(sd_netlink_message *m, unsigned char *flags); +int sd_rtnl_message_addr_get_ifindex(sd_netlink_message *m, int *ifindex); + +int sd_rtnl_message_link_set_flags(sd_netlink_message *m, unsigned flags, unsigned change); +int sd_rtnl_message_link_set_type(sd_netlink_message *m, unsigned type); +int sd_rtnl_message_link_set_family(sd_netlink_message *m, unsigned family); +int sd_rtnl_message_link_get_ifindex(sd_netlink_message *m, int *ifindex); +int sd_rtnl_message_link_get_flags(sd_netlink_message *m, unsigned *flags); +int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned short *type); + +int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen); +int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen); +int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope); +int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags); +int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table); +int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags); +int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family); +int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family); +int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol); +int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope); +int sd_rtnl_message_route_get_tos(sd_netlink_message *m, unsigned char *tos); +int sd_rtnl_message_route_get_table(sd_netlink_message *m, unsigned char *table); +int sd_rtnl_message_route_get_dst_prefixlen(sd_netlink_message *m, unsigned char *dst_len); +int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len); + +int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags); +int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state); +int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family); +int sd_rtnl_message_neigh_get_ifindex(sd_netlink_message *m, int *family); +int sd_rtnl_message_neigh_get_state(sd_netlink_message *m, uint16_t *state); +int sd_rtnl_message_neigh_get_flags(sd_netlink_message *m, uint8_t *flags); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink, sd_netlink_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink_message, sd_netlink_message_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-network.h b/src/libsystemd/include/systemd/sd-network.h new file mode 100644 index 0000000000..0f13e2bae7 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-network.h @@ -0,0 +1,176 @@ +#ifndef foosdnetworkhfoo +#define foosdnetworkhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + Copyright 2014 Tom Gundersen + + 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 . +***/ + +#include +#include + +#include "_sd-common.h" + +/* + * A few points: + * + * Instead of returning an empty string array or empty integer array, we + * may return NULL. + * + * Free the data the library returns with libc free(). String arrays + * are NULL terminated, and you need to free the array itself in + * addition to the strings contained. + * + * We return error codes as negative errno, kernel-style. On success, we + * return 0 or positive. + * + * These functions access data in /run. This is a virtual file system; + * therefore, accesses are relatively cheap. + * + * See sd-network(3) for more information. + */ + +_SD_BEGIN_DECLARATIONS; + +/* Get overall operational state + * Possible states: down, up, dormant, carrier, degraded, routable + * Possible return codes: + * -ENODATA: networkd is not aware of any links + */ +int sd_network_get_operational_state(char **state); + +/* Get DNS entries for all links. These are string representations of + * IP addresses */ +int sd_network_get_dns(char ***dns); + +/* Get NTP entries for all links. These are domain names or string + * representations of IP addresses */ +int sd_network_get_ntp(char ***ntp); + +/* Get the search domains for all links. */ +int sd_network_get_search_domains(char ***domains); + +/* Get the search domains for all links. */ +int sd_network_get_route_domains(char ***domains); + +/* Get setup state from ifindex. + * Possible states: + * pending: udev is still processing the link, we don't yet know if we will manage it + * failed: networkd failed to manage the link + * configuring: in the process of retrieving configuration or configuring the link + * configured: link configured successfully + * unmanaged: networkd is not handling the link + * linger: the link is gone, but has not yet been dropped by networkd + * Possible return codes: + * -ENODATA: networkd is not aware of the link + */ +int sd_network_link_get_setup_state(int ifindex, char **state); + +/* Get operational state from ifindex. + * Possible states: + * off: the device is powered down + * no-carrier: the device is powered up, but it does not yet have a carrier + * dormant: the device has a carrier, but is not yet ready for normal traffic + * carrier: the link has a carrier + * degraded: the link has carrier and addresses valid on the local link configured + * routable: the link has carrier and routable address configured + * Possible return codes: + * -ENODATA: networkd is not aware of the link + */ +int sd_network_link_get_operational_state(int ifindex, char **state); + +/* Get path to .network file applied to link */ +int sd_network_link_get_network_file(int ifindex, char **filename); + +/* Get DNS entries for a given link. These are string representations of + * IP addresses */ +int sd_network_link_get_dns(int ifindex, char ***ret); + +/* Get NTP entries for a given link. These are domain names or string + * representations of IP addresses */ +int sd_network_link_get_ntp(int ifindex, char ***ret); + +/* Indicates whether or not LLMNR should be enabled for the link + * Possible levels of support: yes, no, resolve + * Possible return codes: + * -ENODATA: networkd is not aware of the link + */ +int sd_network_link_get_llmnr(int ifindex, char **llmnr); + +/* Indicates whether or not MulticastDNS should be enabled for the + * link. + * Possible levels of support: yes, no, resolve + * Possible return codes: + * -ENODATA: networkd is not aware of the link + */ +int sd_network_link_get_mdns(int ifindex, char **mdns); + +/* Indicates whether or not DNSSEC should be enabled for the link + * Possible levels of support: yes, no, allow-downgrade + * Possible return codes: + * -ENODATA: networkd is not aware of the link + */ +int sd_network_link_get_dnssec(int ifindex, char **dnssec); + +/* Returns the list of per-interface DNSSEC negative trust anchors + * Possible return codes: + * -ENODATA: networkd is not aware of the link, or has no such data + */ +int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta); + +/* Get the search DNS domain names for a given link. */ +int sd_network_link_get_search_domains(int ifindex, char ***domains); + +/* Get the route DNS domain names for a given link. */ +int sd_network_link_get_route_domains(int ifindex, char ***domains); + +/* Get the carrier interface indexes to which current link is bound to. */ +int sd_network_link_get_carrier_bound_to(int ifindex, int **ifindexes); + +/* Get the CARRIERS that are bound to current link. */ +int sd_network_link_get_carrier_bound_by(int ifindex, int **ifindexes); + +/* Get the timezone that was learnt on a specific link. */ +int sd_network_link_get_timezone(int ifindex, char **timezone); + +/* Monitor object */ +typedef struct sd_network_monitor sd_network_monitor; + +/* Create a new monitor. Category must be NULL, "links" or "leases". */ +int sd_network_monitor_new(sd_network_monitor **ret, const char *category); + +/* Destroys the passed monitor. Returns NULL. */ +sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m); + +/* Flushes the monitor */ +int sd_network_monitor_flush(sd_network_monitor *m); + +/* Get FD from monitor */ +int sd_network_monitor_get_fd(sd_network_monitor *m); + +/* Get poll() mask to monitor */ +int sd_network_monitor_get_events(sd_network_monitor *m); + +/* Get timeout for poll(), as usec value relative to CLOCK_MONOTONIC's epoch */ +int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_network_monitor, sd_network_monitor_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-path.h b/src/libsystemd/include/systemd/sd-path.h new file mode 100644 index 0000000000..be6abdcd03 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-path.h @@ -0,0 +1,91 @@ +#ifndef foosdpathhfoo +#define foosdpathhfoo + +/*** + This file is part of systemd. + + Copyright 2014 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 . +***/ + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + /* Temporary files */ + SD_PATH_TEMPORARY = 0x0ULL, + SD_PATH_TEMPORARY_LARGE, + + /* Vendor supplied data */ + SD_PATH_SYSTEM_BINARIES, + SD_PATH_SYSTEM_INCLUDE, + SD_PATH_SYSTEM_LIBRARY_PRIVATE, + SD_PATH_SYSTEM_LIBRARY_ARCH, + SD_PATH_SYSTEM_SHARED, + SD_PATH_SYSTEM_CONFIGURATION_FACTORY, + SD_PATH_SYSTEM_STATE_FACTORY, + + /* System configuration, runtime, state, ... */ + SD_PATH_SYSTEM_CONFIGURATION, + SD_PATH_SYSTEM_RUNTIME, + SD_PATH_SYSTEM_RUNTIME_LOGS, + SD_PATH_SYSTEM_STATE_PRIVATE, + SD_PATH_SYSTEM_STATE_LOGS, + SD_PATH_SYSTEM_STATE_CACHE, + SD_PATH_SYSTEM_STATE_SPOOL, + + /* Vendor supplied data */ + SD_PATH_USER_BINARIES, + SD_PATH_USER_LIBRARY_PRIVATE, + SD_PATH_USER_LIBRARY_ARCH, + SD_PATH_USER_SHARED, + + /* User configuration, state, runtime ... */ + SD_PATH_USER_CONFIGURATION, /* takes both actual configuration (like /etc) and state (like /var/lib) */ + SD_PATH_USER_RUNTIME, + SD_PATH_USER_STATE_CACHE, + + /* User resources */ + SD_PATH_USER, /* $HOME itself */ + SD_PATH_USER_DOCUMENTS, + SD_PATH_USER_MUSIC, + SD_PATH_USER_PICTURES, + SD_PATH_USER_VIDEOS, + SD_PATH_USER_DOWNLOAD, + SD_PATH_USER_PUBLIC, + SD_PATH_USER_TEMPLATES, + SD_PATH_USER_DESKTOP, + + /* Search paths */ + SD_PATH_SEARCH_BINARIES, + SD_PATH_SEARCH_LIBRARY_PRIVATE, + SD_PATH_SEARCH_LIBRARY_ARCH, + SD_PATH_SEARCH_SHARED, + SD_PATH_SEARCH_CONFIGURATION_FACTORY, + SD_PATH_SEARCH_STATE_FACTORY, + SD_PATH_SEARCH_CONFIGURATION, + + _SD_PATH_MAX, +}; + +int sd_path_home(uint64_t type, const char *suffix, char **path); +int sd_path_search(uint64_t type, const char *suffix, char ***paths); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-resolve.h b/src/libsystemd/include/systemd/sd-resolve.h new file mode 100644 index 0000000000..fe3b910671 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-resolve.h @@ -0,0 +1,117 @@ +#ifndef foosdresolvehfoo +#define foosdresolvehfoo + +/*** + This file is part of systemd. + + Copyright 2005-2014 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 . +***/ + +#include +#include +#include +#include + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* An opaque sd-resolve session structure */ +typedef struct sd_resolve sd_resolve; + +/* An opaque sd-resolve query structure */ +typedef struct sd_resolve_query sd_resolve_query; + +/* A callback on completion */ +typedef int (*sd_resolve_getaddrinfo_handler_t)(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata); +typedef int (*sd_resolve_getnameinfo_handler_t)(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata); + +enum { + SD_RESOLVE_GET_HOST = UINT64_C(1), + SD_RESOLVE_GET_SERVICE = UINT64_C(2), + SD_RESOLVE_GET_BOTH = UINT64_C(3), +}; + +int sd_resolve_default(sd_resolve **ret); + +/* Allocate a new sd-resolve session. */ +int sd_resolve_new(sd_resolve **ret); + +/* Free a sd-resolve session. This destroys all attached + * sd_resolve_query objects automatically. */ +sd_resolve* sd_resolve_unref(sd_resolve *resolve); +sd_resolve* sd_resolve_ref(sd_resolve *resolve); + +/* Return the UNIX file descriptor to poll() for events on. Use this + * function to integrate sd-resolve with your custom main loop. */ +int sd_resolve_get_fd(sd_resolve *resolve); + +/* Return the poll() events (a combination of flags like POLLIN, + * POLLOUT, ...) to check for. */ +int sd_resolve_get_events(sd_resolve *resolve); + +/* Return the poll() timeout to pass. Returns (uint64_t) -1 as + * timeout if no timeout is needed. */ +int sd_resolve_get_timeout(sd_resolve *resolve, uint64_t *timeout_usec); + +/* Process pending responses. After this function is called, you can + * get the next completed query object(s) using + * sd_resolve_get_next(). */ +int sd_resolve_process(sd_resolve *resolve); + +/* Wait for a resolve event to complete. */ +int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec); + +int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid); + +int sd_resolve_attach_event(sd_resolve *resolve, sd_event *e, int64_t priority); +int sd_resolve_detach_event(sd_resolve *resolve); +sd_event *sd_resolve_get_event(sd_resolve *resolve); + +/* Issue a name-to-address query on the specified session. The + * arguments are compatible with those of libc's + * getaddrinfo(3). The function returns a new query object. When the + * query is completed, you may retrieve the results using + * sd_resolve_getaddrinfo_done(). */ +int sd_resolve_getaddrinfo(sd_resolve *resolve, sd_resolve_query **q, const char *node, const char *service, const struct addrinfo *hints, sd_resolve_getaddrinfo_handler_t callback, void *userdata); + +/* Issue an address-to-name query on the specified session. The + * arguments are compatible with those of libc's + * getnameinfo(3). The function returns a new query object. When the + * query is completed, you may retrieve the results using + * sd_resolve_getnameinfo_done(). Set gethost (resp. getserv) to non-zero + * if you want to query the hostname (resp. the service name). */ +int sd_resolve_getnameinfo(sd_resolve *resolve, sd_resolve_query **q, const struct sockaddr *sa, socklen_t salen, int flags, uint64_t get, sd_resolve_getnameinfo_handler_t callback, void *userdata); + +sd_resolve_query *sd_resolve_query_ref(sd_resolve_query* q); +sd_resolve_query *sd_resolve_query_unref(sd_resolve_query* q); + +/* Returns non-zero when the query operation specified by q has been completed. */ +int sd_resolve_query_is_done(sd_resolve_query*q); + +void *sd_resolve_query_get_userdata(sd_resolve_query *q); +void *sd_resolve_query_set_userdata(sd_resolve_query *q, void *userdata); + +sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_resolve, sd_resolve_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_resolve_query, sd_resolve_query_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-utf8.h b/src/libsystemd/include/systemd/sd-utf8.h new file mode 100644 index 0000000000..6781983878 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-utf8.h @@ -0,0 +1,32 @@ +#ifndef foosdutf8hfoo +#define foosdutf8hfoo + +/*** + 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 . +***/ + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +_sd_pure_ const char *sd_utf8_is_valid(const char *s); +_sd_pure_ const char *sd_ascii_is_valid(const char *s); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/sd-bus/DIFFERENCES b/src/libsystemd/sd-bus/DIFFERENCES deleted file mode 100644 index db269675a7..0000000000 --- a/src/libsystemd/sd-bus/DIFFERENCES +++ /dev/null @@ -1,25 +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. - -- NameOwnerChanged is a synthetic message, generated locally and not - by the driver. On dbus1 only the Disconnected message was - synthesized like this. - -- 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 deleted file mode 100644 index 6aeb11364a..0000000000 --- a/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION +++ /dev/null @@ -1,110 +0,0 @@ -How we use GVariant for serializing D-Bus messages --------------------------------------------------- - -We stay close to the original dbus1 framing as possible, but make -certain changes to adapt for GVariant. 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 32bit Serial number - - = 12 bytes - -This header is then followed by the fields array, whose first value is -a 32bit array size. - -When using GVariant we keep the basic structure in place, only -slightly alter 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 Reserved, must be 0 - t 64bit Cookie - - = 16 bytes - -This is then followed by the GVariant fields array ("a{tv}"), and -finally the actual body as variant (v). Putting this altogether a -packet on dbus2 hence qualifies as a fully compliant GVariant -structure of (yyyyuta{tv}v). - -For details on gvariant, see: - -https://people.gnome.org/~desrt/gvariant-serialisation.pdf - -Regarding the framing of dbus2, also see: - -https://wiki.gnome.org/Projects/GLib/GDBus/Version2 - -The first four bytes of the header are defined the same way for dbus1 -and dbus2. The first bytes contain the endianess field and the -protocol version, so that the remainder of the message can be safely -made sense of just by looking at the first 32bit. - -Note that the length of the body is no longer included in the header -on dbus2! In fact, the message size must be known in advance, from the -underlying transport in order to parse dbus2 messages, while it is -directly included in dbus1 message headers. This change of semantics -is an effect of GVariant's basic design. - -The serial number has been renamed cookie and has been extended from -32bit to 64bit. It is recommended to avoid the higher 32bit of the -cookie field though, to simplify compatibility with dbus1 peers. Note -that not only the cookie/serial field in the fixed header, but also -the reply_cookie/reply_serial additional header field has been -increased from 32bit to 64bit, too! - -The header field identifiers have been extended from 8bit to -64bit. This has been done to simplify things (as kdbus otherwise uses -exclusively 64bit types, unless there is a strong reason not to), and -has no effect on the serialization size, as due to alignment for each -8bit header field identifier 56 bits of padding had to be added. - -Note that the header size changed, due to these changes. However, -consider that on dbus1 the beginning of the fields array contains the -32bit array size (since that is how arrays are encoded on dbus1), -thus, if one considers that size part of the header, instead of the -array, the size of the header on dbus1 and dbus2 stays identical, at -16 bytes. - - 0 4 8 12 16 - Common: | E | T | F | V | ... - - dbus1: | (as above) | Body Length | Serial | Fields Length | Fields array ... - - gvariant: | (as above) | Reserved | Cookie | Fields array ... - -And that's already it. - -Note: to simplify parsing, valid kdbus/dbus2 messages must include the -entire fixed header and additional header fields in a single non-memfd -message part. Also, the signature string of the body variant all the -way to the end of the message must be in a single non-memfd part -too. The parts for this extended header and footer can be the same -one, and can also continue any amount of additional body bytes. - -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 deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-bus/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-bus/PORTING-DBUS1 b/src/libsystemd/sd-bus/PORTING-DBUS1 deleted file mode 100644 index 2dedb28bcf..0000000000 --- a/src/libsystemd/sd-bus/PORTING-DBUS1 +++ /dev/null @@ -1,535 +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 /sys/fs/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 /sys/fs/kdbus/0-system, and for user -buses the device node is /sys/fs/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. When a -client encountes such an "incompatible" feature it should immediately -try the next bus address configured in the bus address string. - -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 ":1.". - -The kernel will also return the bloom filter size and bloom filter -hash function number 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_FILTER, -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_FDS: for sending over fds attach an item of this type with - an array of fds. - - KDBUS_ITEM_BLOOM_FILTER: the calculated bloom filter of this message, - only for undirected (broadcast) message. - - KDBUS_ITEM_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 basic types - nor arrays of fixed types. The latter is necessary to allow APIs - to return direct pointers to linear arrays of numeric - values. Examples: The basic types "u", "s", "t" have to be in the - same payload item. The array of fixed 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 > 512K (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 > 512K. 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 < 512K, then simply send -the data as PAYLOAD_VEC and reuse the memfd. If it is >= 512K, 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 parameters for the bloom filters that need to be included in -broadcast message is communicated to userspace as part of the hello -response structure (see above). By default it has the parameters m=512 -(bits in the filter), k=8 (nr of hash functions). Note however, that -this is subject to change in later versions, and userspace -implementations must be capable of handling m values between at least -m=8 and m=2^32, and k values between at least k=1 and k=32. The -underlying hash function is SipHash-2-4. It is used with a number of -constant (yet originally randomly generated) 128bit hash keys, more -specifically: - - b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15, - aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b, - 63,fd,ae,be,cd,82,48,12,a1,6e,41,26,cb,fa,a0,c8, - 23,be,45,29,32,d2,46,2d,82,03,52,28,fe,37,17,f5, - 56,3b,bf,ee,5a,4f,43,39,af,aa,94,08,df,f0,fc,10, - 31,80,c8,73,c7,ea,46,d3,aa,25,75,0f,9e,4c,09,29, - 7d,f7,18,4b,7b,a4,44,d5,85,3c,06,e0,65,53,96,6d, - f2,77,e9,6f,93,b5,4e,71,9a,0c,34,88,39,25,bf,35 - -When calculating the first bit index into the bloom filter, the -SipHash-2-4 hash value is calculated for the input data and the first -16 bytes of the array above as hash key. Of the resulting 8 bytes of -output, as many full bytes are taken for the bit index as necessary, -starting from the output's first byte. For the second bit index the -same hash value is used, continuing with the next unused output byte, -and so on. Each time the bytes returned by the hash function are -depleted it is recalculated with the next 16 byte hash key from the -array above and the same input data. - -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). - -If a client connects to a bus whose bloom filter metrics (i.e. filter -size and number of hash functions) are outside of the range the client -supports it must immediately disconnect and continue connection with -the next bus address of the bus connection string. - -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_ID_ADD/KDBUS_ITEM_ID_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 - -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's domain 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=/sys/fs/kdbus/0-system/bus;unix:path=/var/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=/sys/fs/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 deleted file mode 100644 index 112769fcb6..0000000000 --- a/src/libsystemd/sd-bus/bus-bloom.c +++ /dev/null @@ -1,156 +0,0 @@ -/*** - 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 . -***/ - -#include "bus-bloom.h" -#include "siphash24.h" -#include "util.h" - -static inline void set_bit(uint64_t filter[], unsigned long b) { - filter[b >> 6] |= 1ULL << (b & 63); -} - -static const sd_id128_t hash_keys[] = { - SD_ID128_ARRAY(b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15), - SD_ID128_ARRAY(aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b), - SD_ID128_ARRAY(63,fd,ae,be,cd,82,48,12,a1,6e,41,26,cb,fa,a0,c8), - SD_ID128_ARRAY(23,be,45,29,32,d2,46,2d,82,03,52,28,fe,37,17,f5), - SD_ID128_ARRAY(56,3b,bf,ee,5a,4f,43,39,af,aa,94,08,df,f0,fc,10), - SD_ID128_ARRAY(31,80,c8,73,c7,ea,46,d3,aa,25,75,0f,9e,4c,09,29), - SD_ID128_ARRAY(7d,f7,18,4b,7b,a4,44,d5,85,3c,06,e0,65,53,96,6d), - SD_ID128_ARRAY(f2,77,e9,6f,93,b5,4e,71,9a,0c,34,88,39,25,bf,35), -}; - -static void bloom_add_data( - uint64_t filter[], /* The filter bits */ - size_t size, /* Size of the filter in bytes */ - unsigned k, /* Number of hash functions */ - const void *data, /* Data to hash */ - size_t n) { /* Size of data to hash in bytes */ - - uint64_t h; - uint64_t m; - unsigned w, i, c = 0; - unsigned hash_index; - - assert(size > 0); - assert(k > 0); - - /* Determine bits in filter */ - m = size * 8; - - /* Determine how many bytes we need to generate a bit index 0..m for this filter */ - w = (u64log2(m) + 7) / 8; - - assert(w <= sizeof(uint64_t)); - - /* Make sure we have enough hash keys to generate m * k bits - * of hash value. Note that SipHash24 generates 64 bits of - * hash value for each 128 bits of hash key. */ - assert(k * w <= ELEMENTSOF(hash_keys) * 8); - - for (i = 0, hash_index = 0; i < k; i++) { - uint64_t p = 0; - unsigned d; - - for (d = 0; d < w; d++) { - if (c <= 0) { - h = siphash24(data, n, hash_keys[hash_index++].bytes); - c += 8; - } - - p = (p << 8ULL) | (uint64_t) ((uint8_t *)&h)[8 - c]; - c--; - } - - p &= m - 1; - set_bit(filter, p); - } - - /* log_debug("bloom: adding <%.*s>", (int) n, (char*) data); */ -} - -void bloom_add_pair(uint64_t filter[], size_t size, unsigned k, 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, size, k, c, n); -} - -void bloom_add_prefixes(uint64_t filter[], size_t size, unsigned k, 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); - - bloom_add_data(filter, size, k, c, n); - - for (;;) { - char *e; - - e = strrchr(p, sep); - if (!e) - break; - - *(e + 1) = 0; - bloom_add_data(filter, size, k, c, e - c + 1); - - if (e == p) - break; - - *e = 0; - bloom_add_data(filter, size, k, c, e - c); - } -} - -bool bloom_validate_parameters(size_t size, unsigned k) { - uint64_t m; - unsigned w; - - if (size <= 0) - return false; - - if (k <= 0) - return false; - - m = size * 8; - w = (u64log2(m) + 7) / 8; - if (w > sizeof(uint64_t)) - return false; - - if (k * w > ELEMENTSOF(hash_keys) * 8) - return false; - - return true; -} diff --git a/src/libsystemd/sd-bus/bus-bloom.h b/src/libsystemd/sd-bus/bus-bloom.h deleted file mode 100644 index c824622b95..0000000000 --- a/src/libsystemd/sd-bus/bus-bloom.h +++ /dev/null @@ -1,43 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -/* - * Our default bloom filter has the following parameters: - * - * m=512 (bits in the filter) - * k=8 (hash functions) - * - * We use SipHash24 as hash function with a number of (originally - * randomized) but fixed hash keys. - * - */ - -#define DEFAULT_BLOOM_SIZE (512/8) /* m: filter size */ -#define DEFAULT_BLOOM_N_HASH 8 /* k: number of hash functions */ - -void bloom_add_pair(uint64_t filter[], size_t size, unsigned n_hash, const char *a, const char *b); -void bloom_add_prefixes(uint64_t filter[], size_t size, unsigned n_hash, const char *a, const char *b, char sep); - -bool bloom_validate_parameters(size_t size, unsigned n_hash); diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c deleted file mode 100644 index 02e3bf904c..0000000000 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ /dev/null @@ -1,87 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 . -***/ - -#include - -#include "sd-bus.h" - -#include "bus-common-errors.h" -#include "bus-error.h" - -BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_UNIT, ENOENT), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_UNIT_FOR_PID, ESRCH), - SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_EXISTS, EEXIST), - SD_BUS_ERROR_MAP(BUS_ERROR_LOAD_FAILED, EIO), - SD_BUS_ERROR_MAP(BUS_ERROR_JOB_FAILED, EREMOTEIO), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_JOB, ENOENT), - SD_BUS_ERROR_MAP(BUS_ERROR_NOT_SUBSCRIBED, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_ALREADY_SUBSCRIBED, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_ONLY_BY_DEPENDENCY, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, EDEADLK), - SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLK), - SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLK), - SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ESHUTDOWN), - SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_GENERATED, EADDRNOTAVAIL), - SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_LINKED, ELOOP), - SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM), - SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED), - SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN), - - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_MACHINE_FOR_PID, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_MACHINE_EXISTS, EEXIST), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRIVATE_NETWORKING, ENOSYS), - - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SESSION, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SESSION_FOR_PID, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_USER, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_USER_FOR_PID, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SEAT, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_SESSION_NOT_ON_SEAT, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_NOT_IN_CONTROL, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_IS_TAKEN, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_NOT_TAKEN, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_OPERATION_IN_PROGRESS, EINPROGRESS), - SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP), - - SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY), - - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_PROCESS, ESRCH), - - SD_BUS_ERROR_MAP(BUS_ERROR_NO_NAME_SERVERS, ESRCH), - SD_BUS_ERROR_MAP(BUS_ERROR_INVALID_REPLY, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_RR, ENOENT), - SD_BUS_ERROR_MAP(BUS_ERROR_CNAME_LOOP, EDEADLK), - SD_BUS_ERROR_MAP(BUS_ERROR_ABORTED, ECANCELED), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SERVICE, EUNATCH), - SD_BUS_ERROR_MAP(BUS_ERROR_DNSSEC_FAILED, EHOSTUNREACH), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_TRUST_ANCHOR, EHOSTUNREACH), - SD_BUS_ERROR_MAP(BUS_ERROR_RR_TYPE_UNSUPPORTED, EOPNOTSUPP), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_LINK, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY), - SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN), - - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY), - - SD_BUS_ERROR_MAP_END -}; diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h deleted file mode 100644 index c8f369cb78..0000000000 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ /dev/null @@ -1,86 +0,0 @@ -#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 . -***/ - -#include "bus-error.h" - -#define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit" -#define BUS_ERROR_NO_UNIT_FOR_PID "org.freedesktop.systemd1.NoUnitForPID" -#define BUS_ERROR_UNIT_EXISTS "org.freedesktop.systemd1.UnitExists" -#define BUS_ERROR_LOAD_FAILED "org.freedesktop.systemd1.LoadFailed" -#define BUS_ERROR_JOB_FAILED "org.freedesktop.systemd1.JobFailed" -#define BUS_ERROR_NO_SUCH_JOB "org.freedesktop.systemd1.NoSuchJob" -#define BUS_ERROR_NOT_SUBSCRIBED "org.freedesktop.systemd1.NotSubscribed" -#define BUS_ERROR_ALREADY_SUBSCRIBED "org.freedesktop.systemd1.AlreadySubscribed" -#define BUS_ERROR_ONLY_BY_DEPENDENCY "org.freedesktop.systemd1.OnlyByDependency" -#define BUS_ERROR_TRANSACTION_JOBS_CONFLICTING "org.freedesktop.systemd1.TransactionJobsConflicting" -#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic" -#define BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE "org.freedesktop.systemd1.TransactionIsDestructive" -#define BUS_ERROR_UNIT_MASKED "org.freedesktop.systemd1.UnitMasked" -#define BUS_ERROR_UNIT_GENERATED "org.freedesktop.systemd1.UnitGenerated" -#define BUS_ERROR_UNIT_LINKED "org.freedesktop.systemd1.UnitLinked" -#define BUS_ERROR_JOB_TYPE_NOT_APPLICABLE "org.freedesktop.systemd1.JobTypeNotApplicable" -#define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation" -#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown" -#define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning" - -#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine" -#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage" -#define BUS_ERROR_NO_MACHINE_FOR_PID "org.freedesktop.machine1.NoMachineForPID" -#define BUS_ERROR_MACHINE_EXISTS "org.freedesktop.machine1.MachineExists" -#define BUS_ERROR_NO_PRIVATE_NETWORKING "org.freedesktop.machine1.NoPrivateNetworking" -#define BUS_ERROR_NO_SUCH_USER_MAPPING "org.freedesktop.machine1.NoSuchUserMapping" -#define BUS_ERROR_NO_SUCH_GROUP_MAPPING "org.freedesktop.machine1.NoSuchGroupMapping" - -#define BUS_ERROR_NO_SUCH_SESSION "org.freedesktop.login1.NoSuchSession" -#define BUS_ERROR_NO_SESSION_FOR_PID "org.freedesktop.login1.NoSessionForPID" -#define BUS_ERROR_NO_SUCH_USER "org.freedesktop.login1.NoSuchUser" -#define BUS_ERROR_NO_USER_FOR_PID "org.freedesktop.login1.NoUserForPID" -#define BUS_ERROR_NO_SUCH_SEAT "org.freedesktop.login1.NoSuchSeat" -#define BUS_ERROR_SESSION_NOT_ON_SEAT "org.freedesktop.login1.SessionNotOnSeat" -#define BUS_ERROR_NOT_IN_CONTROL "org.freedesktop.login1.NotInControl" -#define BUS_ERROR_DEVICE_IS_TAKEN "org.freedesktop.login1.DeviceIsTaken" -#define BUS_ERROR_DEVICE_NOT_TAKEN "org.freedesktop.login1.DeviceNotTaken" -#define BUS_ERROR_OPERATION_IN_PROGRESS "org.freedesktop.login1.OperationInProgress" -#define BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED "org.freedesktop.login1.SleepVerbNotSupported" -#define BUS_ERROR_SESSION_BUSY "org.freedesktop.login1.SessionBusy" - -#define BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED "org.freedesktop.timedate1.AutomaticTimeSyncEnabled" - -#define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess" - -#define BUS_ERROR_NO_NAME_SERVERS "org.freedesktop.resolve1.NoNameServers" -#define BUS_ERROR_INVALID_REPLY "org.freedesktop.resolve1.InvalidReply" -#define BUS_ERROR_NO_SUCH_RR "org.freedesktop.resolve1.NoSuchRR" -#define BUS_ERROR_CNAME_LOOP "org.freedesktop.resolve1.CNameLoop" -#define BUS_ERROR_ABORTED "org.freedesktop.resolve1.Aborted" -#define BUS_ERROR_NO_SUCH_SERVICE "org.freedesktop.resolve1.NoSuchService" -#define BUS_ERROR_DNSSEC_FAILED "org.freedesktop.resolve1.DnssecFailed" -#define BUS_ERROR_NO_TRUST_ANCHOR "org.freedesktop.resolve1.NoTrustAnchor" -#define BUS_ERROR_RR_TYPE_UNSUPPORTED "org.freedesktop.resolve1.ResourceRecordTypeUnsupported" -#define BUS_ERROR_NO_SUCH_LINK "org.freedesktop.resolve1.NoSuchLink" -#define BUS_ERROR_LINK_BUSY "org.freedesktop.resolve1.LinkBusy" -#define BUS_ERROR_NETWORK_DOWN "org.freedesktop.resolve1.NetworkDown" -#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError." - -#define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer" -#define BUS_ERROR_TRANSFER_IN_PROGRESS "org.freedesktop.import1.TransferInProgress" - -BUS_ERROR_MAP_ELF_USE(bus_common_errors); diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c deleted file mode 100644 index 3191d27ded..0000000000 --- a/src/libsystemd/sd-bus/bus-container.c +++ /dev/null @@ -1,277 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#include "bus-container.h" -#include "bus-internal.h" -#include "bus-socket.h" -#include "fd-util.h" -#include "process-util.h" -#include "util.h" - -int bus_container_connect_socket(sd_bus *b) { - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; - pid_t child; - siginfo_t si; - int r, error_buf = 0; - ssize_t n; - - assert(b); - assert(b->input_fd < 0); - assert(b->output_fd < 0); - assert(b->nspid > 0 || b->machine); - - if (b->nspid <= 0) { - r = container_get_leader(b->machine, &b->nspid); - if (r < 0) - return r; - } - - r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &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); - - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return -errno; - - if (child == 0) { - pid_t grandchild; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, 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 SO_PEERCRED of our connection - * comes from a process from within the container, and - * not outside of it */ - - grandchild = fork(); - if (grandchild < 0) - _exit(EXIT_FAILURE); - - if (grandchild == 0) { - - r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); - if (r < 0) { - /* Try to send error up */ - error_buf = errno; - (void) write(pair[1], &error_buf, sizeof(error_buf)); - _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); - } - - pair[1] = safe_close(pair[1]); - - r = wait_for_terminate(child, &si); - if (r < 0) - return r; - - n = read(pair[0], &error_buf, sizeof(error_buf)); - if (n < 0) - return -errno; - - if (n > 0) { - if (n != sizeof(error_buf)) - return -EIO; - - if (error_buf < 0) - return -EIO; - - if (error_buf == EINPROGRESS) - return 1; - - if (error_buf > 0) - return -error_buf; - } - - if (si.si_code != CLD_EXITED) - return -EIO; - - if (si.si_status != EXIT_SUCCESS) - return -EIO; - - return bus_socket_start_auth(b); -} - -int bus_container_connect_kernel(sd_bus *b) { - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - int error_buf = 0; - struct iovec iov = { - .iov_base = &error_buf, - .iov_len = sizeof(error_buf), - }; - struct msghdr mh = { - .msg_control = &control, - .msg_controllen = sizeof(control), - .msg_iov = &iov, - .msg_iovlen = 1, - }; - struct cmsghdr *cmsg; - pid_t child; - siginfo_t si; - int r, fd = -1; - ssize_t n; - - assert(b); - assert(b->input_fd < 0); - assert(b->output_fd < 0); - assert(b->nspid > 0 || b->machine); - - if (b->nspid <= 0) { - r = container_get_leader(b->machine, &b->nspid); - if (r < 0) - return r; - } - - r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); - if (r < 0) - return r; - - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return -errno; - - if (child == 0) { - pid_t grandchild; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, 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) { - /* Try to send error up */ - error_buf = errno; - (void) write(pair[1], &error_buf, sizeof(error_buf)); - _exit(EXIT_FAILURE); - } - - r = send_one_fd(pair[1], fd, 0); - if (r < 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); - } - - pair[1] = safe_close(pair[1]); - - r = wait_for_terminate(child, &si); - if (r < 0) - return r; - - n = recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); - if (n < 0) - return -errno; - - CMSG_FOREACH(cmsg, &mh) { - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - int *fds; - unsigned n_fds; - - assert(fd < 0); - - 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]; - } - } - - /* If there's an fd passed, we are good. */ - if (fd >= 0) { - b->input_fd = b->output_fd = fd; - return bus_kernel_take_fd(b); - } - - /* If there's an error passed, use it */ - if (n == sizeof(error_buf) && error_buf > 0) - return -error_buf; - - /* Otherwise, we have no clue */ - return -EIO; -} diff --git a/src/libsystemd/sd-bus/bus-container.h b/src/libsystemd/sd-bus/bus-container.h deleted file mode 100644 index 509ef45624..0000000000 --- a/src/libsystemd/sd-bus/bus-container.h +++ /dev/null @@ -1,25 +0,0 @@ -#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 . -***/ - -#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 deleted file mode 100644 index 52128e7b5c..0000000000 --- a/src/libsystemd/sd-bus/bus-control.c +++ /dev/null @@ -1,1588 +0,0 @@ -/*** - 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 . -***/ - -#ifdef HAVE_VALGRIND_MEMCHECK_H -#include -#endif - -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-bloom.h" -#include "bus-control.h" -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-util.h" -#include "capability-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" -#include "user-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); - - if (!bus->bus_client) - return -EINVAL; - - 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 *n; - size_t size, l; - int r; - - assert(bus); - assert(name); - - l = strlen(name) + 1; - size = offsetof(struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(l); - n = alloca0_align(size, 8); - n->size = size; - n->flags = request_name_flags_to_kdbus(flags); - - n->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; - n->items[0].type = KDBUS_ITEM_NAME; - memcpy(n->items[0].str, name, l); - -#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->return_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_(sd_bus_message_unrefp) 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_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->bus_client) - return -EINVAL; - - /* Don't allow requesting the special driver and local names */ - if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - 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 *n; - size_t size, l; - int r; - - assert(bus); - assert(name); - - l = strlen(name) + 1; - size = offsetof(struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(l); - n = alloca0_align(size, 8); - n->size = size; - - n->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; - n->items[0].type = KDBUS_ITEM_NAME; - memcpy(n->items[0].str, name, l); - -#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 0; -} - -static int bus_release_name_dbus1(sd_bus *bus, const char *name) { - _cleanup_(sd_bus_message_unrefp) 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_pid_changed(bus), -ECHILD); - assert_return(service_name_is_valid(name), -EINVAL); - assert_return(name[0] != ':', -EINVAL); - - if (!bus->bus_client) - return -EINVAL; - - /* Don't allow releasing the special driver and local names */ - if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - 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_list cmd = { - .size = sizeof(cmd), - .flags = flags, - }; - struct kdbus_info *name_list, *name; - uint64_t previous_id = 0; - int r; - - /* Caller will free half-constructed list on failure... */ - - r = ioctl(bus->input_fd, KDBUS_CMD_LIST, &cmd); - if (r < 0) - return -errno; - - name_list = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd.offset); - - KDBUS_FOREACH(name, name_list, cmd.list_size) { - struct kdbus_item *item; - - if ((flags & KDBUS_LIST_UNIQUE) && name->id != previous_id && !(name->flags & KDBUS_HELLO_ACTIVATOR)) { - char *n; - - if (asprintf(&n, ":1.%llu", (unsigned long long) name->id) < 0) { - r = -ENOMEM; - goto fail; - } - - r = strv_consume(x, n); - if (r < 0) - goto fail; - - previous_id = name->id; - } - - KDBUS_ITEM_FOREACH(item, name, items) { - if (item->type == KDBUS_ITEM_OWNED_NAME) { - if (service_name_is_valid(item->name.name)) { - r = strv_extend(x, item->name.name); - if (r < 0) { - r = -ENOMEM; - goto fail; - } - } - } - } - } - - r = 0; - -fail: - bus_kernel_cmd_free(bus, cmd.offset); - return r; -} - -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_LIST_UNIQUE | KDBUS_LIST_NAMES, &x); - if (r < 0) - return r; - } - - if (activatable) { - r = kernel_get_list(bus, KDBUS_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_(sd_bus_message_unrefp) 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_pid_changed(bus), -ECHILD); - - if (!bus->bus_client) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (bus->is_kernel) - return bus_list_names_kernel(bus, acquired, activatable); - else - return bus_list_names_dbus1(bus, acquired, activatable); -} - -static int bus_populate_creds_from_items( - sd_bus *bus, - struct kdbus_info *info, - uint64_t mask, - sd_bus_creds *c) { - - struct kdbus_item *item; - uint64_t m; - int r; - - assert(bus); - assert(info); - assert(c); - - KDBUS_ITEM_FOREACH(item, info, items) { - - switch (item->type) { - - case KDBUS_ITEM_PIDS: - - if (mask & SD_BUS_CREDS_PID && item->pids.pid > 0) { - c->pid = (pid_t) item->pids.pid; - c->mask |= SD_BUS_CREDS_PID; - } - - if (mask & SD_BUS_CREDS_TID && item->pids.tid > 0) { - c->tid = (pid_t) item->pids.tid; - c->mask |= SD_BUS_CREDS_TID; - } - - if (mask & SD_BUS_CREDS_PPID) { - if (item->pids.ppid > 0) { - c->ppid = (pid_t) item->pids.ppid; - c->mask |= SD_BUS_CREDS_PPID; - } else if (item->pids.pid == 1) { - /* The structure doesn't - * really distinguish the case - * where a process has no - * parent and where we don't - * know it because it could - * not be translated due to - * namespaces. However, we - * know that PID 1 has no - * parent process, hence let's - * patch that in, manually. */ - c->ppid = 0; - c->mask |= SD_BUS_CREDS_PPID; - } - } - - break; - - case KDBUS_ITEM_CREDS: - - if (mask & SD_BUS_CREDS_UID && (uid_t) item->creds.uid != UID_INVALID) { - c->uid = (uid_t) item->creds.uid; - c->mask |= SD_BUS_CREDS_UID; - } - - if (mask & SD_BUS_CREDS_EUID && (uid_t) item->creds.euid != UID_INVALID) { - c->euid = (uid_t) item->creds.euid; - c->mask |= SD_BUS_CREDS_EUID; - } - - if (mask & SD_BUS_CREDS_SUID && (uid_t) item->creds.suid != UID_INVALID) { - c->suid = (uid_t) item->creds.suid; - c->mask |= SD_BUS_CREDS_SUID; - } - - if (mask & SD_BUS_CREDS_FSUID && (uid_t) item->creds.fsuid != UID_INVALID) { - c->fsuid = (uid_t) item->creds.fsuid; - c->mask |= SD_BUS_CREDS_FSUID; - } - - if (mask & SD_BUS_CREDS_GID && (gid_t) item->creds.gid != GID_INVALID) { - c->gid = (gid_t) item->creds.gid; - c->mask |= SD_BUS_CREDS_GID; - } - - if (mask & SD_BUS_CREDS_EGID && (gid_t) item->creds.egid != GID_INVALID) { - c->egid = (gid_t) item->creds.egid; - c->mask |= SD_BUS_CREDS_EGID; - } - - if (mask & SD_BUS_CREDS_SGID && (gid_t) item->creds.sgid != GID_INVALID) { - c->sgid = (gid_t) item->creds.sgid; - c->mask |= SD_BUS_CREDS_SGID; - } - - if (mask & SD_BUS_CREDS_FSGID && (gid_t) item->creds.fsgid != GID_INVALID) { - c->fsgid = (gid_t) item->creds.fsgid; - c->mask |= SD_BUS_CREDS_FSGID; - } - - break; - - case KDBUS_ITEM_PID_COMM: - if (mask & SD_BUS_CREDS_COMM) { - r = free_and_strdup(&c->comm, item->str); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_COMM; - } - break; - - case KDBUS_ITEM_TID_COMM: - if (mask & SD_BUS_CREDS_TID_COMM) { - r = free_and_strdup(&c->tid_comm, item->str); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_TID_COMM; - } - break; - - case KDBUS_ITEM_EXE: - if (mask & SD_BUS_CREDS_EXE) { - r = free_and_strdup(&c->exe, item->str); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_EXE; - } - break; - - case KDBUS_ITEM_CMDLINE: - if (mask & SD_BUS_CREDS_CMDLINE) { - c->cmdline_size = item->size - offsetof(struct kdbus_item, data); - c->cmdline = memdup(item->data, c->cmdline_size); - if (!c->cmdline) - return -ENOMEM; - - 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) { - r = free_and_strdup(&c->cgroup, item->str); - if (r < 0) - return r; - - r = bus_get_root_path(bus); - if (r < 0) - return r; - - r = free_and_strdup(&c->cgroup_root, bus->cgroup_root); - if (r < 0) - return r; - - 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) { - if (item->caps.last_cap != cap_last_cap() || - item->size - offsetof(struct kdbus_item, caps.caps) < DIV_ROUND_UP(item->caps.last_cap, 32U) * 4 * 4) - return -EBADMSG; - - c->capability = memdup(item->caps.caps, item->size - offsetof(struct kdbus_item, caps.caps)); - if (!c->capability) - return -ENOMEM; - - c->mask |= m; - } - break; - - case KDBUS_ITEM_SECLABEL: - if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) { - r = free_and_strdup(&c->label, item->str); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; - } - break; - - case KDBUS_ITEM_AUDIT: - if (mask & SD_BUS_CREDS_AUDIT_SESSION_ID) { - c->audit_session_id = (uint32_t) item->audit.sessionid; - c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; - } - - if (mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) { - c->audit_login_uid = (uid_t) item->audit.loginuid; - c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; - } - break; - - case KDBUS_ITEM_OWNED_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) - return r; - - c->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; - } - break; - - case KDBUS_ITEM_CONN_DESCRIPTION: - if (mask & SD_BUS_CREDS_DESCRIPTION) { - r = free_and_strdup(&c->description, item->str); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_DESCRIPTION; - } - break; - - case KDBUS_ITEM_AUXGROUPS: - if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - size_t i, n; - uid_t *g; - - n = (item->size - offsetof(struct kdbus_item, data64)) / sizeof(uint64_t); - g = new(gid_t, n); - if (!g) - return -ENOMEM; - - for (i = 0; i < n; i++) - g[i] = item->data64[i]; - - free(c->supplementary_gids); - c->supplementary_gids = g; - c->n_supplementary_gids = n; - - c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; - } - break; - } - } - - return 0; -} - -int bus_get_name_creds_kdbus( - sd_bus *bus, - const char *name, - uint64_t mask, - bool allow_activator, - sd_bus_creds **creds) { - - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; - struct kdbus_cmd_info *cmd; - struct kdbus_info *conn_info; - size_t size, l; - uint64_t id; - int r; - - if (streq(name, "org.freedesktop.DBus")) - return -EOPNOTSUPP; - - r = bus_kernel_parse_unique_name(name, &id); - if (r < 0) - return r; - if (r > 0) { - size = offsetof(struct kdbus_cmd_info, items); - cmd = alloca0_align(size, 8); - cmd->id = id; - } else { - l = strlen(name) + 1; - size = offsetof(struct kdbus_cmd_info, items) + KDBUS_ITEM_SIZE(l); - cmd = alloca0_align(size, 8); - cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; - cmd->items[0].type = KDBUS_ITEM_NAME; - memcpy(cmd->items[0].str, name, l); - } - - /* If augmentation is on, and the bus didn't provide us - * the bits we want, then ask for the PID/TID so that we - * can read the rest from /proc. */ - if ((mask & SD_BUS_CREDS_AUGMENT) && - (mask & (SD_BUS_CREDS_PPID| - SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| - SD_BUS_CREDS_SUPPLEMENTARY_GIDS| - SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_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_SELINUX_CONTEXT| - SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))) - mask |= SD_BUS_CREDS_PID; - - cmd->size = size; - cmd->attach_flags = attach_flags_to_kdbus(mask); - - r = ioctl(bus->input_fd, KDBUS_CMD_CONN_INFO, cmd); - if (r < 0) - return -errno; - - conn_info = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd->offset); - - /* Non-activated names are considered not available */ - if (!allow_activator && (conn_info->flags & KDBUS_HELLO_ACTIVATOR)) { - if (name[0] == ':') - r = -ENXIO; - else - r = -ESRCH; - goto fail; - } - - c = bus_creds_new(); - if (!c) { - r = -ENOMEM; - goto fail; - } - - if (mask & SD_BUS_CREDS_UNIQUE_NAME) { - if (asprintf(&c->unique_name, ":1.%llu", (unsigned long long) conn_info->id) < 0) { - r = -ENOMEM; - goto fail; - } - - c->mask |= SD_BUS_CREDS_UNIQUE_NAME; - } - - /* If KDBUS_ITEM_OWNED_NAME is requested then we'll get 0 of - them in case the service has no names. This does not mean - however that the list of owned names could not be - acquired. Hence, let's explicitly clarify that the data is - complete. */ - c->mask |= mask & SD_BUS_CREDS_WELL_KNOWN_NAMES; - - r = bus_populate_creds_from_items(bus, conn_info, mask, c); - if (r < 0) - goto fail; - - r = bus_creds_add_more(c, mask, 0, 0); - if (r < 0) - goto fail; - - if (creds) { - *creds = c; - c = NULL; - } - - r = 0; - -fail: - bus_kernel_cmd_free(bus, cmd->offset); - return r; -} - -static int bus_get_name_creds_dbus1( - sd_bus *bus, - const char *name, - uint64_t mask, - sd_bus_creds **creds) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_unique = NULL, *reply = NULL; - _cleanup_(sd_bus_creds_unrefp) 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) || - ((mask & SD_BUS_CREDS_AUGMENT) && - (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| - SD_BUS_CREDS_SUPPLEMENTARY_GIDS| - 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_SELINUX_CONTEXT| - 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_EUID) { - 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->euid = u; - c->mask |= SD_BUS_CREDS_EUID; - - reply = sd_bus_message_unref(reply); - } - - if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const void *p = NULL; - size_t sz = 0; - - r = sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetConnectionSELinuxSecurityContext", - &error, - &reply, - "s", - unique ? unique : name); - if (r < 0) { - if (!sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown")) - return r; - } else { - 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_name_creds( - 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_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); - assert_return(mask == 0 || creds, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(service_name_is_valid(name), -EINVAL); - - if (!bus->bus_client) - return -EINVAL; - - if (streq(name, "org.freedesktop.DBus.Local")) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (bus->is_kernel) - return bus_get_name_creds_kdbus(bus, name, mask, false, creds); - else - return bus_get_name_creds_dbus1(bus, name, mask, creds); -} - -static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; - struct kdbus_cmd_info cmd = { - .size = sizeof(struct kdbus_cmd_info), - }; - struct kdbus_info *creator_info; - pid_t pid = 0; - int r; - - c = bus_creds_new(); - if (!c) - return -ENOMEM; - - /* If augmentation is on, and the bus doesn't didn't allow us - * to get the bits we want, then ask for the PID/TID so that we - * can read the rest from /proc. */ - if ((mask & SD_BUS_CREDS_AUGMENT) && - (mask & (SD_BUS_CREDS_PPID| - SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| - SD_BUS_CREDS_SUPPLEMENTARY_GIDS| - SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_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_SELINUX_CONTEXT| - SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))) - mask |= SD_BUS_CREDS_PID; - - cmd.attach_flags = attach_flags_to_kdbus(mask); - - r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); - if (r < 0) - return -errno; - - creator_info = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd.offset); - - r = bus_populate_creds_from_items(bus, creator_info, mask, c); - bus_kernel_cmd_free(bus, cmd.offset); - if (r < 0) - return r; - - r = bus_creds_add_more(c, mask, pid, 0); - if (r < 0) - return r; - - *ret = c; - c = NULL; - return 0; -} - -static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; - pid_t pid = 0; - bool do_label; - int r; - - assert(bus); - - do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT); - - /* Avoid allocating anything if we have no chance of returning useful data */ - if (!bus->ucred_valid && !do_label) - return -ENODATA; - - c = bus_creds_new(); - if (!c) - return -ENOMEM; - - if (bus->ucred_valid) { - if (bus->ucred.pid > 0) { - pid = c->pid = bus->ucred.pid; - c->mask |= SD_BUS_CREDS_PID & mask; - } - - if (bus->ucred.uid != UID_INVALID) { - c->euid = bus->ucred.uid; - c->mask |= SD_BUS_CREDS_EUID & mask; - } - - if (bus->ucred.gid != GID_INVALID) { - c->egid = bus->ucred.gid; - c->mask |= SD_BUS_CREDS_EGID & mask; - } - } - - if (do_label) { - c->label = strdup(bus->label); - 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; - - *ret = c; - c = NULL; - return 0; -} - -_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { - assert_return(bus, -EINVAL); - assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); - assert_return(ret, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (bus->is_kernel) - return bus_get_owner_creds_kdbus(bus, mask, ret); - else - return bus_get_owner_creds_dbus1(bus, mask, ret); -} - -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_align(sz, 8); - 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.id = old_owner_id; - item->name_change.new_id.id = new_owner_id; - - memcpy_safe(item->name_change.name, name, l); - - /* If the old name is unset or empty, then - * this can match against added names */ - if (isempty(old_owner)) { - 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 (isempty(new_owner)) { - 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_align(sz, 8); - 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 (isempty(old_owner)) { - item->type = KDBUS_ITEM_ID_ADD; - if (!isempty(new_owner)) - item->id_change.id = new_owner_id; - - 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 (isempty(new_owner)) { - item->type = KDBUS_ITEM_ID_REMOVE; - if (!isempty(old_owner)) - item->id_change.id = old_owner_id; - - 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, - struct bus_match_component *components, - unsigned n_components, - uint64_t cookie) { - - struct kdbus_cmd_match *m; - struct kdbus_item *item; - uint64_t *bloom; - size_t sz; - const char *sender = NULL; - size_t sender_length = 0; - uint64_t src_id = KDBUS_MATCH_ID_ANY, dst_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); - - /* Monitor streams don't support matches, make this a NOP */ - if (bus->hello_flags & KDBUS_HELLO_MONITOR) - return 0; - - bloom = alloca0(bus->bloom_size); - - 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, bus->bloom_size, bus->bloom_n_hash, "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, bus->bloom_size, bus->bloom_n_hash, "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, bus->bloom_size, bus->bloom_n_hash, "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, bus->bloom_size, bus->bloom_n_hash, "path", c->value_str); - using_bloom = true; - break; - - case BUS_MATCH_PATH_NAMESPACE: - bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "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; - - xsprintf(buf, "arg%i", c->type - BUS_MATCH_ARG); - bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); - using_bloom = true; - break; - } - - case BUS_MATCH_ARG_HAS...BUS_MATCH_ARG_HAS_LAST: { - char buf[sizeof("arg")-1 + 2 + sizeof("-has")]; - - xsprintf(buf, "arg%i-has", c->type - BUS_MATCH_ARG_HAS); - bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); - using_bloom = true; - break; - } - - case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST: - /* - * XXX: DBus spec defines arg[0..63]path= matching to be - * a two-way glob. That is, if either string is a prefix - * of the other, it matches. - * This is really hard to realize in bloom-filters, as - * we would have to create a bloom-match for each prefix - * of @c->value_str. This is excessive, hence we just - * ignore all those matches and accept everything from - * the kernel. People should really avoid those matches. - * If they're used in real-life some day, we will have - * to properly support multiple-matches here. - */ - break; - - case BUS_MATCH_ARG_NAMESPACE...BUS_MATCH_ARG_NAMESPACE_LAST: { - char buf[sizeof("arg")-1 + 2 + sizeof("-dot-prefix")]; - - xsprintf(buf, "arg%i-dot-prefix", c->type - BUS_MATCH_ARG_NAMESPACE); - bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); - using_bloom = true; - break; - } - - case BUS_MATCH_DESTINATION: - /* - * Kernel only supports matching on destination IDs, but - * not on destination names. So just skip the - * destination name restriction and verify it in - * user-space on retrieval. - */ - r = bus_kernel_parse_unique_name(c->value_str, &dst_id); - if (r < 0) - return r; - else if (r > 0) - sz += ALIGN8(offsetof(struct kdbus_item, id) + sizeof(uint64_t)); - - /* if not a broadcast, it cannot be a name-change */ - if (r <= 0 || dst_id != KDBUS_DST_ID_BROADCAST) - matches_name_change = false; - - 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) + bus->bloom_size); - - m = alloca0_align(sz, 8); - m->size = sz; - m->cookie = cookie; - - 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 (dst_id != KDBUS_MATCH_ID_ANY) { - item->size = offsetof(struct kdbus_item, id) + sizeof(uint64_t); - item->type = KDBUS_ITEM_DST_ID; - item->id = dst_id; - item = KDBUS_ITEM_NEXT(item); - } - - if (using_bloom) { - item->size = offsetof(struct kdbus_item, data64) + bus->bloom_size; - item->type = KDBUS_ITEM_BLOOM_MASK; - memcpy(item->data64, bloom, bus->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; -} - -#define internal_match(bus, m) \ - ((bus)->hello_flags & KDBUS_HELLO_MONITOR \ - ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \ - : (m)) - -static int bus_add_match_internal_dbus1( - sd_bus *bus, - const char *match) { - - const char *e; - - assert(bus); - assert(match); - - e = internal_match(bus, match); - - return sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "AddMatch", - NULL, - NULL, - "s", - e); -} - -int bus_add_match_internal( - sd_bus *bus, - const char *match, - struct bus_match_component *components, - unsigned n_components, - uint64_t cookie) { - - assert(bus); - - if (!bus->bus_client) - return -EINVAL; - - if (bus->is_kernel) - return bus_add_match_internal_kernel(bus, components, n_components, cookie); - else - return bus_add_match_internal_dbus1(bus, match); -} - -int bus_remove_match_internal_kernel( - sd_bus *bus, - uint64_t cookie) { - - struct kdbus_cmd_match m = { - .size = offsetof(struct kdbus_cmd_match, items), - .cookie = cookie, - }; - int r; - - assert(bus); - - /* Monitor streams don't support matches, make this a NOP */ - if (bus->hello_flags & KDBUS_HELLO_MONITOR) - return 0; - - 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) { - - const char *e; - - assert(bus); - assert(match); - - e = internal_match(bus, match); - - return sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "RemoveMatch", - NULL, - NULL, - "s", - e); -} - -int bus_remove_match_internal( - sd_bus *bus, - const char *match, - uint64_t cookie) { - - assert(bus); - - if (!bus->bus_client) - return -EINVAL; - - if (bus->is_kernel) - return bus_remove_match_internal_kernel(bus, cookie); - else - return bus_remove_match_internal_dbus1(bus, match); -} - -_public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) { - _cleanup_(sd_bus_message_unrefp) 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_pid_changed(bus), -ECHILD); - assert_return(service_name_is_valid(name), -EINVAL); - - if (!bus->bus_client) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (streq_ptr(name, bus->unique_name)) - return sd_id128_get_machine(machine); - - r = sd_bus_message_new_method_call( - bus, - &m, - name, - "/", - "org.freedesktop.DBus.Peer", - "GetMachineId"); - if (r < 0) - return r; - - r = sd_bus_message_set_auto_start(m, false); - 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 deleted file mode 100644 index c181aa7959..0000000000 --- a/src/libsystemd/sd-bus/bus-control.h +++ /dev/null @@ -1,32 +0,0 @@ -#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 . -***/ - -#include "sd-bus.h" - -#include "bus-match.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, struct bus_match_component *components, unsigned n_components, uint64_t cookie); -int bus_remove_match_internal_kernel(sd_bus *bus, uint64_t cookie); - -int bus_get_name_creds_kdbus(sd_bus *bus, const char *name, uint64_t mask, bool allow_activator, sd_bus_creds **creds); diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c deleted file mode 100644 index 2d06bf541f..0000000000 --- a/src/libsystemd/sd-bus/bus-convenience.c +++ /dev/null @@ -1,626 +0,0 @@ -/*** - 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 . -***/ - -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-signature.h" -#include "bus-type.h" -#include "bus-util.h" -#include "string-util.h" - -_public_ int sd_bus_emit_signal( - sd_bus *bus, - const char *path, - const char *interface, - const char *member, - const char *types, ...) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - r = sd_bus_message_new_signal(bus, &m, path, interface, member); - 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_async( - sd_bus *bus, - sd_bus_slot **slot, - const char *destination, - const char *path, - const char *interface, - const char *member, - sd_bus_message_handler_t callback, - void *userdata, - const char *types, ...) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member); - 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_async(bus, slot, m, callback, userdata, 0); -} - -_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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - bus_assert_return(bus, -EINVAL, error); - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member); - if (r < 0) - goto fail; - - if (!isempty(types)) { - va_list ap; - - va_start(ap, types); - r = bus_message_append_ap(m, types, ap); - va_end(ap); - if (r < 0) - goto fail; - } - - return sd_bus_call(bus, m, 0, error, reply); - -fail: - return sd_bus_error_set_errno(error, r); -} - -_public_ int sd_bus_reply_method_return( - sd_bus_message *call, - const char *types, ...) { - - _cleanup_(sd_bus_message_unrefp) 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, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - 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_(sd_bus_message_unrefp) 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, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 0; - - r = sd_bus_message_new_method_error(call, &m, e); - 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_(sd_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, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - 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_(sd_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, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - 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_(sd_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, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 0; - - va_start(ap, format); - sd_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; - - bus_assert_return(bus, -EINVAL, error); - bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); - bus_assert_return(member_name_is_valid(member), -EINVAL, error); - bus_assert_return(reply, -EINVAL, error); - bus_assert_return(signature_is_single(type, false), -EINVAL, error); - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - 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); - goto fail; - } - - *reply = rep; - return 0; - -fail: - return sd_bus_error_set_errno(error, r); -} - -_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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - bus_assert_return(bus, -EINVAL, error); - bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); - bus_assert_return(member_name_is_valid(member), -EINVAL, error); - bus_assert_return(bus_type_is_trivial(type), -EINVAL, error); - bus_assert_return(ptr, -EINVAL, error); - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - 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) - goto fail; - - r = sd_bus_message_read_basic(reply, type, ptr); - if (r < 0) - goto fail; - - return 0; - -fail: - return sd_bus_error_set_errno(error, r); -} - -_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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *s; - char *n; - int r; - - bus_assert_return(bus, -EINVAL, error); - bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); - bus_assert_return(member_name_is_valid(member), -EINVAL, error); - bus_assert_return(ret, -EINVAL, error); - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - 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) - goto fail; - - r = sd_bus_message_read_basic(reply, 's', &s); - if (r < 0) - goto fail; - - n = strdup(s); - if (!n) { - r = -ENOMEM; - goto fail; - } - - *ret = n; - return 0; - -fail: - return sd_bus_error_set_errno(error, r); -} - -_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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - bus_assert_return(bus, -EINVAL, error); - bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); - bus_assert_return(member_name_is_valid(member), -EINVAL, error); - bus_assert_return(ret, -EINVAL, error); - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - 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) - goto fail; - - r = sd_bus_message_read_strv(reply, ret); - if (r < 0) - goto fail; - - return 0; - -fail: - return sd_bus_error_set_errno(error, r); -} - -_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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - va_list ap; - int r; - - bus_assert_return(bus, -EINVAL, error); - bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); - bus_assert_return(member_name_is_valid(member), -EINVAL, error); - bus_assert_return(signature_is_single(type, false), -EINVAL, error); - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set"); - if (r < 0) - goto fail; - - r = sd_bus_message_append(m, "ss", strempty(interface), member); - if (r < 0) - goto fail; - - r = sd_bus_message_open_container(m, 'v', type); - if (r < 0) - goto fail; - - va_start(ap, type); - r = bus_message_append_ap(m, type, ap); - va_end(ap); - if (r < 0) - goto fail; - - r = sd_bus_message_close_container(m); - if (r < 0) - goto fail; - - return sd_bus_call(bus, m, 0, error, NULL); - -fail: - return sd_bus_error_set_errno(error, r); -} - -_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, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - 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) - /* There's a sender, but the creds are - * missing. This means we are talking via - * dbus1, or are getting a message that was - * sent to us via kdbus, but was converted - * from a dbus1 message by the bus-proxy and - * thus also lacks the creds. */ - return sd_bus_get_name_creds(call->bus, call->sender, mask, creds); - else - /* There's no sender, hence we are on a dbus1 - * direct connection. For direct connections - * the credentials of the AF_UNIX peer matter, - * which may be queried via - * sd_bus_get_owner_creds(). */ - return sd_bus_get_owner_creds(call->bus, mask, creds); - } - - return bus_creds_extend_by_pid(c, mask, creds); -} - -_public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - uid_t our_uid; - bool know_caps = false; - int r; - - assert_return(call, -EINVAL); - assert_return(call->sealed, -EPERM); - assert_return(call->bus, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - if (capability >= 0) { - - r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds); - if (r < 0) - return r; - - /* We cannot use augmented caps for authorization, - * since then data is acquired raceful from - * /proc. This can never actually happen, but let's - * better be safe than sorry, and do an extra check - * here. */ - assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EFFECTIVE_CAPS) == 0, -EPERM); - - /* Note that not even on kdbus we might have the caps - * field, due to faked identities, or namespace - * translation issues. */ - r = sd_bus_creds_has_effective_cap(creds, capability); - if (r > 0) - return 1; - if (r == 0) - know_caps = true; - } else { - r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - } - - /* Now, check the UID, but only if the capability check wasn't - * sufficient */ - our_uid = getuid(); - if (our_uid != 0 || !know_caps || capability < 0) { - uid_t sender_uid; - - /* We cannot use augmented uid/euid for authorization, - * since then data is acquired raceful from - * /proc. This can never actually happen, but let's - * better be safe than sorry, and do an extra check - * here. */ - assert_return((sd_bus_creds_get_augmented_mask(creds) & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID)) == 0, -EPERM); - - /* Try to use the EUID, if we have it. */ - r = sd_bus_creds_get_euid(creds, &sender_uid); - if (r < 0) - r = sd_bus_creds_get_uid(creds, &sender_uid); - - if (r >= 0) { - /* Sender has same UID as us, then let's grant access */ - if (sender_uid == our_uid) - return 1; - - /* Sender is root, we are not root. */ - if (our_uid != 0 && sender_uid == 0) - return 1; - } - } - - return 0; -} diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c deleted file mode 100644 index c4f693dee9..0000000000 --- a/src/libsystemd/sd-bus/bus-creds.c +++ /dev/null @@ -1,1349 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#include "alloc-util.h" -#include "audit-util.h" -#include "bus-creds.h" -#include "bus-label.h" -#include "bus-message.h" -#include "bus-util.h" -#include "capability-util.h" -#include "cgroup-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "hexdecoct.h" -#include "parse-util.h" -#include "process-util.h" -#include "string-util.h" -#include "strv.h" -#include "terminal-util.h" -#include "user-util.h" -#include "util.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); - free(c->user_slice); - free(c->unescaped_description); - free(c->supplementary_gids); - free(c->tty); - - free(c->well_known_names); /* note that this is an strv, but - * we only free the array, not the - * strings the array points to. The - * full strv we only free if - * c->allocated is set, see - * below. */ - - strv_free(c->cmdline_array); -} - -_public_ sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c) { - - if (!c) - return 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) { - 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->description); - - c->supplementary_gids = mfree(c->supplementary_gids); - - c->well_known_names = strv_free(c->well_known_names); - - bus_creds_done(c); - - 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; -} - -_public_ uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c) { - assert_return(c, 0); - - return c->augmented; -} - -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(sd_bus_creds **ret, pid_t pid, uint64_t mask) { - sd_bus_creds *c; - int r; - - assert_return(pid >= 0, -EINVAL); - assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); - assert_return(ret, -EINVAL); - - if (pid == 0) - pid = getpid(); - - c = bus_creds_new(); - if (!c) - return -ENOMEM; - - r = bus_creds_add_more(c, mask | SD_BUS_CREDS_AUGMENT, 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 (!pid_is_alive(pid)) { - 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_euid(sd_bus_creds *c, uid_t *euid) { - assert_return(c, -EINVAL); - assert_return(euid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_EUID)) - return -ENODATA; - - *euid = c->euid; - return 0; -} - -_public_ int sd_bus_creds_get_suid(sd_bus_creds *c, uid_t *suid) { - assert_return(c, -EINVAL); - assert_return(suid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_SUID)) - return -ENODATA; - - *suid = c->suid; - return 0; -} - - -_public_ int sd_bus_creds_get_fsuid(sd_bus_creds *c, uid_t *fsuid) { - assert_return(c, -EINVAL); - assert_return(fsuid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_FSUID)) - return -ENODATA; - - *fsuid = c->fsuid; - 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_GID)) - return -ENODATA; - - *gid = c->gid; - return 0; -} - -_public_ int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) { - assert_return(c, -EINVAL); - assert_return(egid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_EGID)) - return -ENODATA; - - *egid = c->egid; - return 0; -} - -_public_ int sd_bus_creds_get_sgid(sd_bus_creds *c, gid_t *sgid) { - assert_return(c, -EINVAL); - assert_return(sgid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_SGID)) - return -ENODATA; - - *sgid = c->sgid; - return 0; -} - -_public_ int sd_bus_creds_get_fsgid(sd_bus_creds *c, gid_t *fsgid) { - assert_return(c, -EINVAL); - assert_return(fsgid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_FSGID)) - return -ENODATA; - - *fsgid = c->fsgid; - return 0; -} - -_public_ int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) { - assert_return(c, -EINVAL); - assert_return(gids, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS)) - return -ENODATA; - - *gids = c->supplementary_gids; - return (int) c->n_supplementary_gids; -} - -_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_ppid(sd_bus_creds *c, pid_t *ppid) { - assert_return(c, -EINVAL); - assert_return(ppid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_PPID)) - return -ENODATA; - - /* PID 1 has no parent process. Let's distinguish the case of - * not knowing and not having a parent process by the returned - * error code. */ - if (c->ppid == 0) - return -ENXIO; - - *ppid = c->ppid; - 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_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; - - if (!c->exe) - return -ENXIO; - - *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_user_slice(sd_bus_creds *c, const char **ret) { - int r; - - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_USER_SLICE)) - return -ENODATA; - - assert(c->cgroup); - - if (!c->user_slice) { - const char *shifted; - - r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); - if (r < 0) - return r; - - r = cg_path_get_user_slice(shifted, (char**) &c->user_slice); - if (r < 0) - return r; - } - - *ret = c->user_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; - - if (!c->cmdline) - return -ENXIO; - - 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; - - if (c->audit_session_id == AUDIT_SESSION_INVALID) - return -ENXIO; - - *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; - - if (c->audit_login_uid == UID_INVALID) - return -ENXIO; - - *uid = c->audit_login_uid; - return 0; -} - -_public_ int sd_bus_creds_get_tty(sd_bus_creds *c, const char **ret) { - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_TTY)) - return -ENODATA; - - if (!c->tty) - return -ENXIO; - - *ret = c->tty; - 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; - - /* As a special hack we return the bus driver as well-known - * names list when this is requested. */ - if (c->well_known_names_driver) { - static const char* const wkn[] = { - "org.freedesktop.DBus", - NULL - }; - - *well_known_names = (char**) wkn; - return 0; - } - - if (c->well_known_names_local) { - static const char* const wkn[] = { - "org.freedesktop.DBus.Local", - NULL - }; - - *well_known_names = (char**) wkn; - return 0; - } - - *well_known_names = c->well_known_names; - return 0; -} - -_public_ int sd_bus_creds_get_description(sd_bus_creds *c, const char **ret) { - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_DESCRIPTION)) - return -ENODATA; - - assert(c->description); - - if (!c->unescaped_description) { - c->unescaped_description = bus_label_unescape(c->description); - if (!c->unescaped_description) - return -ENOMEM; - } - - *ret = c->unescaped_description; - return 0; -} - -static int has_cap(sd_bus_creds *c, unsigned offset, int capability) { - size_t sz; - - assert(c); - assert(capability >= 0); - assert(c->capability); - - if ((unsigned) capability > cap_last_cap()) - return 0; - - sz = DIV_ROUND_UP(cap_last_cap(), 32U); - - return !!(c->capability[offset * sz + CAP_TO_INDEX(capability)] & CAP_TO_MASK(capability)); -} - -_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, max; - unsigned i, j; - - assert(c); - assert(p); - - max = DIV_ROUND_UP(cap_last_cap(), 32U); - p += strspn(p, WHITESPACE); - - sz = strlen(p); - if (sz % 8 != 0) - return -EINVAL; - - sz /= 8; - if (sz > max) - return -EINVAL; - - if (!c->capability) { - c->capability = new0(uint32_t, max * 4); - if (!c->capability) - return -ENOMEM; - } - - for (i = 0; i < sz; i ++) { - uint32_t v = 0; - - for (j = 0; j < 8; ++j) { - int t; - - t = unhexchar(*p++); - if (t < 0) - return -EINVAL; - - v = (v << 4) | t; - } - - c->capability[offset * max + (sz - i - 1)] = v; - } - - 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); - - if (!(mask & SD_BUS_CREDS_AUGMENT)) - return 0; - - /* Try to retrieve PID from creds if it wasn't passed to us */ - if (pid > 0) { - c->pid = pid; - c->mask |= SD_BUS_CREDS_PID; - } else if (c->mask & SD_BUS_CREDS_PID) - pid = c->pid; - else - /* Without pid we cannot do much... */ - return 0; - - /* Try to retrieve TID from creds if it wasn't passed to us */ - if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID)) - tid = c->tid; - - /* Calculate what we shall and can add */ - missing = mask & ~(c->mask|SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_DESCRIPTION|SD_BUS_CREDS_AUGMENT); - if (missing == 0) - return 0; - - if (tid > 0) { - c->tid = tid; - c->mask |= SD_BUS_CREDS_TID; - } - - if (missing & (SD_BUS_CREDS_PPID | - SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_SUID | SD_BUS_CREDS_FSUID | - SD_BUS_CREDS_GID | SD_BUS_CREDS_EGID | SD_BUS_CREDS_SGID | SD_BUS_CREDS_FSGID | - SD_BUS_CREDS_SUPPLEMENTARY_GIDS | - 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; - const char *p; - - p = procfs_file_alloca(pid, "status"); - - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - else if (errno != EPERM && errno != EACCES) - return -errno; - } else { - char line[LINE_MAX]; - - FOREACH_LINE(line, f, return -errno) { - truncate_nl(line); - - if (missing & SD_BUS_CREDS_PPID) { - p = startswith(line, "PPid:"); - if (p) { - p += strspn(p, WHITESPACE); - - /* Explicitly check for PPID 0 (which is the case for PID 1) */ - if (!streq(p, "0")) { - r = parse_pid(p, &c->ppid); - if (r < 0) - return r; - - } else - c->ppid = 0; - - c->mask |= SD_BUS_CREDS_PPID; - continue; - } - } - - if (missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID)) { - p = startswith(line, "Uid:"); - if (p) { - unsigned long uid, euid, suid, fsuid; - - p += strspn(p, WHITESPACE); - if (sscanf(p, "%lu %lu %lu %lu", &uid, &euid, &suid, &fsuid) != 4) - return -EIO; - - if (missing & SD_BUS_CREDS_UID) - c->uid = (uid_t) uid; - if (missing & SD_BUS_CREDS_EUID) - c->euid = (uid_t) euid; - if (missing & SD_BUS_CREDS_SUID) - c->suid = (uid_t) suid; - if (missing & SD_BUS_CREDS_FSUID) - c->fsuid = (uid_t) fsuid; - - c->mask |= missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID); - continue; - } - } - - if (missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) { - p = startswith(line, "Gid:"); - if (p) { - unsigned long gid, egid, sgid, fsgid; - - p += strspn(p, WHITESPACE); - if (sscanf(p, "%lu %lu %lu %lu", &gid, &egid, &sgid, &fsgid) != 4) - return -EIO; - - if (missing & SD_BUS_CREDS_GID) - c->gid = (gid_t) gid; - if (missing & SD_BUS_CREDS_EGID) - c->egid = (gid_t) egid; - if (missing & SD_BUS_CREDS_SGID) - c->sgid = (gid_t) sgid; - if (missing & SD_BUS_CREDS_FSGID) - c->fsgid = (gid_t) fsgid; - - c->mask |= missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID); - continue; - } - } - - if (missing & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - p = startswith(line, "Groups:"); - if (p) { - size_t allocated = 0; - - for (;;) { - unsigned long g; - int n = 0; - - p += strspn(p, WHITESPACE); - if (*p == 0) - break; - - if (sscanf(p, "%lu%n", &g, &n) != 1) - return -EIO; - - if (!GREEDY_REALLOC(c->supplementary_gids, allocated, c->n_supplementary_gids+1)) - return -ENOMEM; - - c->supplementary_gids[c->n_supplementary_gids++] = (gid_t) g; - p += n; - } - - c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; - 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_SELINUX_CONTEXT) { - const char *p; - - p = procfs_file_alloca(pid, "attr/current"); - r = read_one_line_file(p, &c->label); - if (r < 0) { - if (r != -ENOENT && r != -EINVAL && r != -EPERM && r != -EACCES) - return r; - } else - c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; - } - - if (missing & SD_BUS_CREDS_COMM) { - r = get_process_comm(pid, &c->comm); - if (r < 0) { - if (r != -EPERM && r != -EACCES) - return r; - } else - c->mask |= SD_BUS_CREDS_COMM; - } - - if (missing & SD_BUS_CREDS_EXE) { - r = get_process_exe(pid, &c->exe); - if (r == -ESRCH) { - /* Unfortunately we cannot really distinguish - * the case here where the process does not - * exist, and /proc/$PID/exe being unreadable - * because $PID is a kernel thread. Hence, - * assume it is a kernel thread, and rely on - * that this case is caught with a later - * call. */ - c->exe = NULL; - c->mask |= SD_BUS_CREDS_EXE; - } else if (r < 0) { - if (r != -EPERM && r != -EACCES) - return r; - } else - 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 == -ENOENT) - return -ESRCH; - if (r < 0) { - if (r != -EPERM && r != -EACCES) - return r; - } else { - if (c->cmdline_size == 0) - c->cmdline = mfree(c->cmdline); - - c->mask |= SD_BUS_CREDS_CMDLINE; - } - } - - if (tid > 0 && (missing & SD_BUS_CREDS_TID_COMM)) { - _cleanup_free_ char *p = NULL; - - if (asprintf(&p, "/proc/"PID_FMT"/task/"PID_FMT"/comm", pid, tid) < 0) - return -ENOMEM; - - r = read_one_line_file(p, &c->tid_comm); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) { - if (r != -EPERM && r != -EACCES) - return r; - } else - 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_USER_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) { - - if (!c->cgroup) { - r = cg_pid_get_path(NULL, pid, &c->cgroup); - if (r < 0) { - if (r != -EPERM && r != -EACCES) - return r; - } - } - - if (!c->cgroup_root) { - r = cg_get_root_path(&c->cgroup_root); - if (r < 0) - return r; - } - - if (c->cgroup) - c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_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 == -ENODATA) { - /* ENODATA means: no audit session id assigned */ - c->audit_session_id = AUDIT_SESSION_INVALID; - c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; - } else if (r < 0) { - if (r != -EOPNOTSUPP && r != -ENOENT && r != -EPERM && r != -EACCES) - return r; - } else - 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 == -ENODATA) { - /* ENODATA means: no audit login uid assigned */ - c->audit_login_uid = UID_INVALID; - c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; - } else if (r < 0) { - if (r != -EOPNOTSUPP && r != -ENOENT && r != -EPERM && r != -EACCES) - return r; - } else - c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; - } - - if (missing & SD_BUS_CREDS_TTY) { - r = get_ctty(pid, NULL, &c->tty); - if (r == -ENXIO) { - /* ENXIO means: process has no controlling TTY */ - c->tty = NULL; - c->mask |= SD_BUS_CREDS_TTY; - } else if (r < 0) { - if (r != -EPERM && r != -EACCES && r != -ENOENT) - return r; - } else - c->mask |= SD_BUS_CREDS_TTY; - } - - /* In case only the exe path was to be read we cannot - * distinguish the case where the exe path was unreadable - * because the process was a kernel thread, or when the - * process didn't exist at all. Hence, let's do a final check, - * to be sure. */ - if (!pid_is_alive(pid)) - return -ESRCH; - - if (tid > 0 && tid != pid && !pid_is_unwaited(tid)) - return -ESRCH; - - c->augmented = missing & c->mask; - - return 0; -} - -int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *n = NULL; - int r; - - assert(c); - assert(ret); - - if ((mask & ~c->mask) == 0 || (!(mask & SD_BUS_CREDS_AUGMENT))) { - /* There's already all data we need, or augmentation - * wasn't turned on. */ - - *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_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_PPID) { - n->ppid = c->ppid; - n->mask |= SD_BUS_CREDS_PPID; - } - - 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_EUID) { - n->euid = c->euid; - n->mask |= SD_BUS_CREDS_EUID; - } - - if (c->mask & mask & SD_BUS_CREDS_SUID) { - n->suid = c->suid; - n->mask |= SD_BUS_CREDS_SUID; - } - - if (c->mask & mask & SD_BUS_CREDS_FSUID) { - n->fsuid = c->fsuid; - n->mask |= SD_BUS_CREDS_FSUID; - } - - 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_EGID) { - n->egid = c->egid; - n->mask |= SD_BUS_CREDS_EGID; - } - - if (c->mask & mask & SD_BUS_CREDS_SGID) { - n->sgid = c->sgid; - n->mask |= SD_BUS_CREDS_SGID; - } - - if (c->mask & mask & SD_BUS_CREDS_FSGID) { - n->fsgid = c->fsgid; - n->mask |= SD_BUS_CREDS_FSGID; - } - - if (c->mask & mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - if (c->supplementary_gids) { - n->supplementary_gids = newdup(gid_t, c->supplementary_gids, c->n_supplementary_gids); - if (!n->supplementary_gids) - return -ENOMEM; - n->n_supplementary_gids = c->n_supplementary_gids; - } else { - n->supplementary_gids = NULL; - n->n_supplementary_gids = 0; - } - - n->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; - } - - if (c->mask & mask & SD_BUS_CREDS_COMM) { - assert(c->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) { - assert(c->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) { - if (c->exe) { - n->exe = strdup(c->exe); - if (!n->exe) - return -ENOMEM; - } else - n->exe = NULL; - - n->mask |= SD_BUS_CREDS_EXE; - } - - if (c->mask & mask & SD_BUS_CREDS_CMDLINE) { - if (c->cmdline) { - n->cmdline = memdup(c->cmdline, c->cmdline_size); - if (!n->cmdline) - return -ENOMEM; - - n->cmdline_size = c->cmdline_size; - } else { - n->cmdline = NULL; - n->cmdline_size = 0; - } - - 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_USER_SLICE|SD_BUS_CREDS_OWNER_UID)) { - assert(c->cgroup); - - 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_USER_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)) { - assert(c->capability); - - n->capability = memdup(c->capability, DIV_ROUND_UP(cap_last_cap(), 32U) * 4 * 4); - if (!n->capability) - return -ENOMEM; - - 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_SELINUX_CONTEXT) { - assert(c->label); - - n->label = strdup(c->label); - if (!n->label) - return -ENOMEM; - n->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; - } - - 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_TTY) { - if (c->tty) { - n->tty = strdup(c->tty); - if (!n->tty) - return -ENOMEM; - } else - n->tty = NULL; - n->mask |= SD_BUS_CREDS_TTY; - } - - if (c->mask & mask & SD_BUS_CREDS_UNIQUE_NAME) { - assert(c->unique_name); - - n->unique_name = strdup(c->unique_name); - if (!n->unique_name) - return -ENOMEM; - n->mask |= SD_BUS_CREDS_UNIQUE_NAME; - } - - if (c->mask & mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { - if (strv_isempty(c->well_known_names)) - n->well_known_names = NULL; - else { - n->well_known_names = strv_copy(c->well_known_names); - if (!n->well_known_names) - return -ENOMEM; - } - n->well_known_names_driver = c->well_known_names_driver; - n->well_known_names_local = c->well_known_names_local; - n->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; - } - - if (c->mask & mask & SD_BUS_CREDS_DESCRIPTION) { - assert(c->description); - n->description = strdup(c->description); - if (!n->description) - return -ENOMEM; - n->mask |= SD_BUS_CREDS_DESCRIPTION; - } - - n->augmented = c->augmented & n->mask; - - /* Get more data */ - - r = bus_creds_add_more(n, mask, 0, 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 deleted file mode 100644 index df8a1f1005..0000000000 --- a/src/libsystemd/sd-bus/bus-creds.h +++ /dev/null @@ -1,90 +0,0 @@ -#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 . -***/ - -#include - -#include "sd-bus.h" - -struct sd_bus_creds { - bool allocated; - unsigned n_ref; - - uint64_t mask; - uint64_t augmented; - - uid_t uid; - uid_t euid; - uid_t suid; - uid_t fsuid; - gid_t gid; - gid_t egid; - gid_t sgid; - gid_t fsgid; - - gid_t *supplementary_gids; - unsigned n_supplementary_gids; - - pid_t ppid; - pid_t pid; - 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; - char *user_slice; - - char *tty; - - uint32_t *capability; - - uint32_t audit_session_id; - uid_t audit_login_uid; - - char *label; - - char *unique_name; - - char **well_known_names; - bool well_known_names_driver:1; - bool well_known_names_local:1; - - char *cgroup_root; - - char *description, *unescaped_description; -}; - -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 deleted file mode 100644 index 21a6b20a11..0000000000 --- a/src/libsystemd/sd-bus/bus-dump.c +++ /dev/null @@ -1,602 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "bus-dump.h" -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-type.h" -#include "cap-list.h" -#include "capability-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "locale-util.h" -#include "macro.h" -#include "string-util.h" -#include "strv.h" -#include "terminal-util.h" -#include "util.h" - -static char *indent(unsigned level, unsigned flags) { - char *p; - unsigned n, i = 0; - - n = 0; - - if (flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY && level > 0) - level -= 1; - - if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) - n += 2; - - p = new(char, n + level*8 + 1); - if (!p) - return NULL; - - if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) { - p[i++] = ' '; - p[i++] = ' '; - } - - memset(p + i, ' ', level*8); - p[i + level*8] = 0; - - return p; -} - -int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) { - unsigned level = 1; - int r; - - assert(m); - - if (!f) - f = stdout; - - if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) { - fprintf(f, - "%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u Priority=%"PRIi64, - 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() : "", special_glyph(TRIANGULAR_BULLET), ansi_normal(), - ansi_highlight(), bus_message_type_to_string(m->header->type), ansi_normal(), - m->header->endian, - m->header->flags, - m->header->version, - m->priority); - - /* 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=%" PRIu64, BUS_MESSAGE_COOKIE(m)); - - if (m->reply_cookie != 0) - fprintf(f, " ReplyCookie=%" PRIu64, m->reply_cookie); - - fputs("\n", f); - - if (m->sender) - fprintf(f, " Sender=%s%s%s", ansi_highlight(), m->sender, ansi_normal()); - if (m->destination) - fprintf(f, " Destination=%s%s%s", ansi_highlight(), m->destination, ansi_normal()); - if (m->path) - fprintf(f, " Path=%s%s%s", ansi_highlight(), m->path, ansi_normal()); - if (m->interface) - fprintf(f, " Interface=%s%s%s", ansi_highlight(), m->interface, ansi_normal()); - if (m->member) - fprintf(f, " Member=%s%s%s", ansi_highlight(), m->member, ansi_normal()); - - 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_normal(), - ansi_highlight_red(), strna(m->error.message), ansi_normal()); - - if (m->monotonic != 0) - fprintf(f, " Monotonic="USEC_FMT, m->monotonic); - if (m->realtime != 0) - fprintf(f, " Realtime="USEC_FMT, m->realtime); - if (m->seqnum != 0) - fprintf(f, " SequenceNumber=%"PRIu64, m->seqnum); - - if (m->monotonic != 0 || m->realtime != 0 || m->seqnum != 0) - fputs("\n", f); - - bus_creds_dump(&m->creds, f, true); - } - - r = sd_bus_message_rewind(m, !(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)); - if (r < 0) - return log_error_errno(r, "Failed to rewind: %m"); - - if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) { - _cleanup_free_ char *prefix = NULL; - - prefix = indent(0, flags); - if (!prefix) - return log_oom(); - - fprintf(f, "%sMESSAGE \"%s\" {\n", prefix, 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) - return log_error_errno(r, "Failed to peek type: %m"); - - if (r == 0) { - if (level <= 1) - break; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return log_error_errno(r, "Failed to exit container: %m"); - - level--; - - prefix = indent(level, flags); - if (!prefix) - return log_oom(); - - fprintf(f, "%s};\n", prefix); - continue; - } - - prefix = indent(level, flags); - if (!prefix) - return log_oom(); - - if (bus_type_is_container(type) > 0) { - r = sd_bus_message_enter_container(m, type, contents); - if (r < 0) - return log_error_errno(r, "Failed to enter container: %m"); - - 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) - return log_error_errno(r, "Failed to get basic: %m"); - - assert(r > 0); - - switch (type) { - - case SD_BUS_TYPE_BYTE: - fprintf(f, "%sBYTE %s%u%s;\n", prefix, ansi_highlight(), basic.u8, ansi_normal()); - break; - - case SD_BUS_TYPE_BOOLEAN: - fprintf(f, "%sBOOLEAN %s%s%s;\n", prefix, ansi_highlight(), true_false(basic.i), ansi_normal()); - break; - - case SD_BUS_TYPE_INT16: - fprintf(f, "%sINT16 %s%i%s;\n", prefix, ansi_highlight(), basic.s16, ansi_normal()); - break; - - case SD_BUS_TYPE_UINT16: - fprintf(f, "%sUINT16 %s%u%s;\n", prefix, ansi_highlight(), basic.u16, ansi_normal()); - break; - - case SD_BUS_TYPE_INT32: - fprintf(f, "%sINT32 %s%i%s;\n", prefix, ansi_highlight(), basic.s32, ansi_normal()); - break; - - case SD_BUS_TYPE_UINT32: - fprintf(f, "%sUINT32 %s%u%s;\n", prefix, ansi_highlight(), basic.u32, ansi_normal()); - break; - - case SD_BUS_TYPE_INT64: - fprintf(f, "%sINT64 %s%"PRIi64"%s;\n", prefix, ansi_highlight(), basic.s64, ansi_normal()); - break; - - case SD_BUS_TYPE_UINT64: - fprintf(f, "%sUINT64 %s%"PRIu64"%s;\n", prefix, ansi_highlight(), basic.u64, ansi_normal()); - break; - - case SD_BUS_TYPE_DOUBLE: - fprintf(f, "%sDOUBLE %s%g%s;\n", prefix, ansi_highlight(), basic.d64, ansi_normal()); - break; - - case SD_BUS_TYPE_STRING: - fprintf(f, "%sSTRING \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal()); - break; - - case SD_BUS_TYPE_OBJECT_PATH: - fprintf(f, "%sOBJECT_PATH \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal()); - break; - - case SD_BUS_TYPE_SIGNATURE: - fprintf(f, "%sSIGNATURE \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal()); - break; - - case SD_BUS_TYPE_UNIX_FD: - fprintf(f, "%sUNIX_FD %s%i%s;\n", prefix, ansi_highlight(), basic.i, ansi_normal()); - break; - - default: - assert_not_reached("Unknown basic type."); - } - } - - if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) { - _cleanup_free_ char *prefix = NULL; - - prefix = indent(0, flags); - if (!prefix) - return log_oom(); - - fprintf(f, "%s};\n\n", prefix); - } - - return 0; -} - -static void dump_capabilities( - sd_bus_creds *c, - FILE *f, - const char *name, - bool terse, - 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%s=%s", terse ? " " : "", name, terse ? "" : ansi_highlight()); - last_cap = cap_last_cap(); - - for (;;) { - if (r > 0) { - - if (n > 0) - fputc(' ', f); - if (n % 4 == 3) - fprintf(f, terse ? "\n " : "\n "); - - fprintf(f, "%s", strna(capability_to_name(i))); - n++; - } - - i++; - - if (i > last_cap) - break; - - r = has(c, i); - } - - fputs("\n", f); - - if (!terse) - fputs(ansi_normal(), f); -} - -int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { - uid_t owner, audit_loginuid; - uint32_t audit_sessionid; - char **cmdline = NULL, **well_known = NULL; - const char *prefix, *color, *suffix, *s; - int r, q, v, w, z; - - assert(c); - - if (!f) - f = stdout; - - if (terse) { - prefix = " "; - suffix = ""; - color = ""; - } else { - const char *off; - - prefix = ""; - color = ansi_highlight(); - - off = ansi_normal(); - suffix = strjoina(off, "\n"); - } - - if (c->mask & SD_BUS_CREDS_PID) - fprintf(f, "%sPID=%s"PID_FMT"%s", prefix, color, c->pid, suffix); - if (c->mask & SD_BUS_CREDS_TID) - fprintf(f, "%sTID=%s"PID_FMT"%s", prefix, color, c->tid, suffix); - if (c->mask & SD_BUS_CREDS_PPID) { - if (c->ppid == 0) - fprintf(f, "%sPPID=%sn/a%s", prefix, color, suffix); - else - fprintf(f, "%sPPID=%s"PID_FMT"%s", prefix, color, c->ppid, suffix); - } - if (c->mask & SD_BUS_CREDS_TTY) - fprintf(f, "%sTTY=%s%s%s", prefix, color, strna(c->tty), suffix); - - if (terse && ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID|SD_BUS_CREDS_TTY)))) - fputs("\n", f); - - if (c->mask & SD_BUS_CREDS_UID) - fprintf(f, "%sUID=%s"UID_FMT"%s", prefix, color, c->uid, suffix); - if (c->mask & SD_BUS_CREDS_EUID) - fprintf(f, "%sEUID=%s"UID_FMT"%s", prefix, color, c->euid, suffix); - if (c->mask & SD_BUS_CREDS_SUID) - fprintf(f, "%sSUID=%s"UID_FMT"%s", prefix, color, c->suid, suffix); - if (c->mask & SD_BUS_CREDS_FSUID) - fprintf(f, "%sFSUID=%s"UID_FMT"%s", prefix, color, c->fsuid, suffix); - r = sd_bus_creds_get_owner_uid(c, &owner); - if (r >= 0) - fprintf(f, "%sOwnerUID=%s"UID_FMT"%s", prefix, color, owner, suffix); - if (c->mask & SD_BUS_CREDS_GID) - fprintf(f, "%sGID=%s"GID_FMT"%s", prefix, color, c->gid, suffix); - if (c->mask & SD_BUS_CREDS_EGID) - fprintf(f, "%sEGID=%s"GID_FMT"%s", prefix, color, c->egid, suffix); - if (c->mask & SD_BUS_CREDS_SGID) - fprintf(f, "%sSGID=%s"GID_FMT"%s", prefix, color, c->sgid, suffix); - if (c->mask & SD_BUS_CREDS_FSGID) - fprintf(f, "%sFSGID=%s"GID_FMT"%s", prefix, color, c->fsgid, suffix); - - if (c->mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - unsigned i; - - fprintf(f, "%sSupplementaryGIDs=%s", prefix, color); - for (i = 0; i < c->n_supplementary_gids; i++) - fprintf(f, "%s" GID_FMT, i > 0 ? " " : "", c->supplementary_gids[i]); - fprintf(f, "%s", suffix); - } - - if (terse && ((c->mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| - SD_BUS_CREDS_SUPPLEMENTARY_GIDS)) || r >= 0)) - fputs("\n", f); - - if (c->mask & SD_BUS_CREDS_COMM) - fprintf(f, "%sComm=%s%s%s", prefix, color, c->comm, suffix); - if (c->mask & SD_BUS_CREDS_TID_COMM) - fprintf(f, "%sTIDComm=%s%s%s", prefix, color, c->tid_comm, suffix); - if (c->mask & SD_BUS_CREDS_EXE) - fprintf(f, "%sExe=%s%s%s", prefix, color, strna(c->exe), suffix); - - if (terse && (c->mask & (SD_BUS_CREDS_EXE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM))) - fputs("\n", f); - - r = sd_bus_creds_get_cmdline(c, &cmdline); - if (r >= 0) { - char **i; - - fprintf(f, "%sCommandLine=%s", prefix, color); - STRV_FOREACH(i, cmdline) { - if (i != cmdline) - fputc(' ', f); - - fputs(*i, f); - } - - fprintf(f, "%s", suffix); - } else if (r != -ENODATA) - fprintf(f, "%sCommandLine=%sn/a%s", prefix, color, suffix); - - if (c->mask & SD_BUS_CREDS_SELINUX_CONTEXT) - fprintf(f, "%sLabel=%s%s%s", prefix, color, c->label, suffix); - if (c->mask & SD_BUS_CREDS_DESCRIPTION) - fprintf(f, "%sDescription=%s%s%s", prefix, color, c->description, suffix); - - if (terse && (c->mask & (SD_BUS_CREDS_SELINUX_CONTEXT|SD_BUS_CREDS_DESCRIPTION))) - fputs("\n", f); - - if (c->mask & SD_BUS_CREDS_CGROUP) - fprintf(f, "%sCGroup=%s%s%s", prefix, color, c->cgroup, suffix); - s = NULL; - r = sd_bus_creds_get_unit(c, &s); - if (r != -ENODATA) - fprintf(f, "%sUnit=%s%s%s", prefix, color, strna(s), suffix); - s = NULL; - v = sd_bus_creds_get_slice(c, &s); - if (v != -ENODATA) - fprintf(f, "%sSlice=%s%s%s", prefix, color, strna(s), suffix); - s = NULL; - q = sd_bus_creds_get_user_unit(c, &s); - if (q != -ENODATA) - fprintf(f, "%sUserUnit=%s%s%s", prefix, color, strna(s), suffix); - s = NULL; - w = sd_bus_creds_get_user_slice(c, &s); - if (w != -ENODATA) - fprintf(f, "%sUserSlice=%s%s%s", prefix, color, strna(s), suffix); - s = NULL; - z = sd_bus_creds_get_session(c, &s); - if (z != -ENODATA) - fprintf(f, "%sSession=%s%s%s", prefix, color, strna(s), suffix); - - if (terse && ((c->mask & SD_BUS_CREDS_CGROUP) || r != -ENODATA || q != -ENODATA || v != -ENODATA || w != -ENODATA || z != -ENODATA)) - fputs("\n", f); - - r = sd_bus_creds_get_audit_login_uid(c, &audit_loginuid); - if (r >= 0) - fprintf(f, "%sAuditLoginUID=%s"UID_FMT"%s", prefix, color, audit_loginuid, suffix); - else if (r != -ENODATA) - fprintf(f, "%sAuditLoginUID=%sn/a%s", prefix, color, suffix); - q = sd_bus_creds_get_audit_session_id(c, &audit_sessionid); - if (q >= 0) - fprintf(f, "%sAuditSessionID=%s%"PRIu32"%s", prefix, color, audit_sessionid, suffix); - else if (q != -ENODATA) - fprintf(f, "%sAuditSessionID=%sn/a%s", prefix, color, suffix); - - if (terse && (r != -ENODATA || q != -ENODATA)) - fputs("\n", f); - - if (c->mask & SD_BUS_CREDS_UNIQUE_NAME) - fprintf(f, "%sUniqueName=%s%s%s", prefix, color, c->unique_name, suffix); - - if (sd_bus_creds_get_well_known_names(c, &well_known) >= 0) { - char **i; - - fprintf(f, "%sWellKnownNames=%s", prefix, color); - STRV_FOREACH(i, well_known) { - if (i != well_known) - fputc(' ', f); - - fputs(*i, f); - } - - fprintf(f, "%s", suffix); - } - - if (terse && (c->mask & SD_BUS_CREDS_UNIQUE_NAME || well_known)) - fputc('\n', f); - - dump_capabilities(c, f, "EffectiveCapabilities", terse, sd_bus_creds_has_effective_cap); - dump_capabilities(c, f, "PermittedCapabilities", terse, sd_bus_creds_has_permitted_cap); - dump_capabilities(c, f, "InheritableCapabilities", terse, sd_bus_creds_has_inheritable_cap); - dump_capabilities(c, f, "BoundingCapabilities", terse, sd_bus_creds_has_bounding_cap); - - return 0; -} - -/* - * For details about the file format, see: - * - * http://wiki.wireshark.org/Development/LibpcapFileFormat - */ - -typedef struct _packed_ pcap_hdr_s { - uint32_t magic_number; /* magic number */ - uint16_t version_major; /* major version number */ - uint16_t version_minor; /* minor version number */ - int32_t thiszone; /* GMT to local correction */ - uint32_t sigfigs; /* accuracy of timestamps */ - uint32_t snaplen; /* max length of captured packets, in octets */ - uint32_t network; /* data link type */ -} pcap_hdr_t ; - -typedef struct _packed_ pcaprec_hdr_s { - uint32_t ts_sec; /* timestamp seconds */ - uint32_t ts_usec; /* timestamp microseconds */ - uint32_t incl_len; /* number of octets of packet saved in file */ - uint32_t orig_len; /* actual length of packet */ -} pcaprec_hdr_t; - -int bus_pcap_header(size_t snaplen, FILE *f) { - - pcap_hdr_t hdr = { - .magic_number = 0xa1b2c3d4U, - .version_major = 2, - .version_minor = 4, - .thiszone = 0, /* UTC */ - .sigfigs = 0, - .network = 231, /* D-Bus */ - }; - - if (!f) - f = stdout; - - assert(snaplen > 0); - assert((size_t) (uint32_t) snaplen == snaplen); - - hdr.snaplen = (uint32_t) snaplen; - - fwrite(&hdr, 1, sizeof(hdr), f); - - return fflush_and_check(f); -} - -int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) { - struct bus_body_part *part; - pcaprec_hdr_t hdr = {}; - struct timeval tv; - unsigned i; - size_t w; - - if (!f) - f = stdout; - - assert(m); - assert(snaplen > 0); - assert((size_t) (uint32_t) snaplen == snaplen); - - if (m->realtime != 0) - timeval_store(&tv, m->realtime); - else - assert_se(gettimeofday(&tv, NULL) >= 0); - - hdr.ts_sec = tv.tv_sec; - hdr.ts_usec = tv.tv_usec; - hdr.orig_len = BUS_MESSAGE_SIZE(m); - hdr.incl_len = MIN(hdr.orig_len, snaplen); - - /* write the pcap header */ - fwrite(&hdr, 1, sizeof(hdr), f); - - /* write the dbus header */ - w = MIN(BUS_MESSAGE_BODY_BEGIN(m), snaplen); - fwrite(m->header, 1, w, f); - snaplen -= w; - - /* write the dbus body */ - MESSAGE_FOREACH_PART(part, i, m) { - if (snaplen <= 0) - break; - - w = MIN(part->size, snaplen); - fwrite(part->data, 1, w, f); - snaplen -= w; - } - - return fflush_and_check(f); -} diff --git a/src/libsystemd/sd-bus/bus-dump.h b/src/libsystemd/sd-bus/bus-dump.h deleted file mode 100644 index 874e86d09c..0000000000 --- a/src/libsystemd/sd-bus/bus-dump.h +++ /dev/null @@ -1,37 +0,0 @@ -#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 . -***/ - -#include -#include - -#include "sd-bus.h" - -enum { - BUS_MESSAGE_DUMP_WITH_HEADER = 1, - BUS_MESSAGE_DUMP_SUBTREE_ONLY = 2, -}; - -int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags); - -int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse); - -int bus_pcap_header(size_t snaplen, FILE *f); -int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f); diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c deleted file mode 100644 index 26219bdeed..0000000000 --- a/src/libsystemd/sd-bus/bus-error.c +++ /dev/null @@ -1,608 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-error.h" -#include "errno-list.h" -#include "string-util.h" -#include "util.h" - -BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = { - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoMemory", ENOMEM), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoServer", EHOSTDOWN), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Timeout", ETIMEDOUT), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoNetwork", ENONET), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Disconnected", ECONNRESET), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidArgs", EINVAL), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileNotFound", ENOENT), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileExists", EEXIST), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownMethod", EBADR), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownObject", EBADR), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownInterface", EBADR), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownProperty", EBADR), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.PropertyReadOnly", EROFS), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidSignature", EINVAL), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidFileContent", EINVAL), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY), - SD_BUS_ERROR_MAP_END -}; - -/* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section. - * Hide them; for currently unknown reasons they get exported to the shared libries - * even without being listed in the sym file. */ -extern const sd_bus_error_map __start_BUS_ERROR_MAP[] _hidden_; -extern const sd_bus_error_map __stop_BUS_ERROR_MAP[] _hidden_; - -/* Additional maps registered with sd_bus_error_add_map() are in this - * NULL terminated array */ -static const sd_bus_error_map **additional_error_maps = NULL; - -static int bus_error_name_to_errno(const char *name) { - const sd_bus_error_map **map, *m; - const char *p; - int r; - - if (!name) - return EINVAL; - - p = startswith(name, "System.Error."); - if (p) { - r = errno_from_name(p); - if (r < 0) - return EIO; - - return r; - } - - if (additional_error_maps) - for (map = additional_error_maps; *map; map++) - for (m = *map;; m++) { - /* For additional error maps the end marker is actually the end marker */ - if (m->code == BUS_ERROR_MAP_END_MARKER) - break; - - if (streq(m->name, name)) - return m->code; - } - - m = __start_BUS_ERROR_MAP; - while (m < __stop_BUS_ERROR_MAP) { - /* For magic ELF error maps, the end marker might - * appear in the middle of things, since multiple maps - * might appear in the same section. Hence, let's skip - * over it, but realign the pointer to the next 8 byte - * boundary, which is the selected alignment for the - * arrays. */ - if (m->code == BUS_ERROR_MAP_END_MARKER) { - m = ALIGN8_PTR(m+1); - continue; - } - - if (streq(m->name, name)) - return m->code; - - m++; - } - - 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 EOPNOTSUPP: - 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) { - assert_return(!bus_error_is_dirty(e), -EINVAL); - - e->name = strdup(name); - if (!e->name) { - *e = BUS_ERROR_OOM; - return -ENOMEM; - } - - /* If we hit OOM on formatting the pretty message, we ignore - * this, since we at least managed to write the error name */ - if (format) - (void) vasprintf((char**) &e->message, format, ap); - - e->_need_free = 1; - } - - 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 (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; -} - -_public_ int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) { - PROTECT_ERRNO; - 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; - - /* Then, let's try to fill in the supplied message */ - - errno = error; /* Make sure that %m resolves to the specified error */ - 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 = sd_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); -} - -static bool map_ok(const sd_bus_error_map *map) { - for (; map->code != BUS_ERROR_MAP_END_MARKER; map++) - if (!map->name || map->code <=0) - return false; - return true; -} - -_public_ int sd_bus_error_add_map(const sd_bus_error_map *map) { - const sd_bus_error_map **maps = NULL; - unsigned n = 0; - - assert_return(map, -EINVAL); - assert_return(map_ok(map), -EINVAL); - - if (additional_error_maps) - for (; additional_error_maps[n] != NULL; n++) - if (additional_error_maps[n] == map) - return 0; - - maps = realloc_multiply(additional_error_maps, sizeof(struct sd_bus_error_map*), n + 2); - if (!maps) - return -ENOMEM; - - maps[n] = map; - maps[n+1] = NULL; - - additional_error_maps = maps; - return 1; -} diff --git a/src/libsystemd/sd-bus/bus-error.h b/src/libsystemd/sd-bus/bus-error.h deleted file mode 100644 index e2c4cf4b3f..0000000000 --- a/src/libsystemd/sd-bus/bus-error.h +++ /dev/null @@ -1,64 +0,0 @@ -#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 . -***/ - -#include - -#include "sd-bus.h" - -#include "macro.h" - -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); - -#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") - -/* - * There are two ways to register error maps with the error translation - * logic: by using BUS_ERROR_MAP_ELF_REGISTER, which however only - * works when linked into the same ELF module, or via - * sd_bus_error_add_map() which is the official, external API, that - * works from any module. - * - * Note that BUS_ERROR_MAP_ELF_REGISTER has to be used as decorator in - * the bus error table, and BUS_ERROR_MAP_ELF_USE has to be used at - * least once per compilation unit (i.e. per library), to ensure that - * the error map is really added to the final binary. - */ - -#define BUS_ERROR_MAP_ELF_REGISTER \ - __attribute__ ((__section__("BUS_ERROR_MAP"))) \ - __attribute__ ((__used__)) \ - __attribute__ ((aligned(8))) - -#define BUS_ERROR_MAP_ELF_USE(errors) \ - extern const sd_bus_error_map errors[]; \ - __attribute__ ((used)) static const sd_bus_error_map * const CONCATENATE(errors ## _copy_, __COUNTER__) = errors; - -/* We use something exotic as end marker, to ensure people build the - * maps using the macsd-ros. */ -#define BUS_ERROR_MAP_END_MARKER -'x' - -BUS_ERROR_MAP_ELF_USE(bus_standard_errors); diff --git a/src/libsystemd/sd-bus/bus-gvariant.c b/src/libsystemd/sd-bus/bus-gvariant.c deleted file mode 100644 index 58782767fa..0000000000 --- a/src/libsystemd/sd-bus/bus-gvariant.c +++ /dev/null @@ -1,311 +0,0 @@ -/*** - 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 . -***/ - -#include "bus-gvariant.h" -#include "bus-signature.h" -#include "bus-type.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: { - if (n == 2) { - /* unary type () has fixed size of 1 */ - r = 1; - } else { - 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; -} - -size_t bus_gvariant_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; -} - -size_t bus_gvariant_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"); -} - -void bus_gvariant_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); -} diff --git a/src/libsystemd/sd-bus/bus-gvariant.h b/src/libsystemd/sd-bus/bus-gvariant.h deleted file mode 100644 index 6da637fb05..0000000000 --- a/src/libsystemd/sd-bus/bus-gvariant.h +++ /dev/null @@ -1,30 +0,0 @@ -#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 . -***/ - -#include "macro.h" - -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_; - -size_t bus_gvariant_determine_word_size(size_t sz, size_t extra); -void bus_gvariant_write_word_le(void *p, size_t sz, size_t value); -size_t bus_gvariant_read_word_le(void *p, size_t sz); diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c deleted file mode 100644 index caca679086..0000000000 --- a/src/libsystemd/sd-bus/bus-internal.c +++ /dev/null @@ -1,374 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-message.h" -#include "hexdecoct.h" -#include "string-util.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; -} - -char* service_name_startswith(const char *a, const char *b) { - const char *p; - - if (!service_name_is_valid(a) || - !service_name_is_valid(b)) - return NULL; - - p = startswith(a, b); - if (!p) - return NULL; - - if (*p == 0) - return (char*) p; - - if (*p == '.') - return (char*) p + 1; - - return NULL; -} - -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; -} - -/* - * Complex pattern match - * This checks whether @a is a 'complex-prefix' of @b, or @b is a - * 'complex-prefix' of @a, based on strings that consist of labels with @c as - * spearator. This function returns true if: - * - both strings are equal - * - either is a prefix of the other and ends with @c - * The second rule makes sure that either string needs to be fully included in - * the other, and the string which is considered the prefix needs to end with a - * separator. - */ -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)); - - 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); -} - -/* - * Simple pattern match - * This checks whether @a is a 'simple-prefix' of @b, based on strings that - * consist of labels with @c as separator. This function returns true, if: - * - if @a and @b are equal - * - if @a is a prefix of @b, and the first following character in @b (or the - * last character in @a) is @c - * The second rule basically makes sure that if @a is a prefix of @b, then @b - * must follow with a new label separated by @c. It cannot extend the label. - */ -static bool simple_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 *a == 0 && (*b == c || separator); - - if (*a == 0) - return true; - - separator = *a == c; - - 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; -} - -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-internal.h b/src/libsystemd/sd-bus/bus-internal.h deleted file mode 100644 index 216d9f62bc..0000000000 --- a/src/libsystemd/sd-bus/bus-internal.h +++ /dev/null @@ -1,399 +0,0 @@ -#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 . -***/ - -#include -#include - -#include "sd-bus.h" - -#include "bus-error.h" -#include "bus-kernel.h" -#include "bus-match.h" -#include "hashmap.h" -#include "kdbus.h" -#include "list.h" -#include "prioq.h" -#include "refcnt.h" -#include "socket-util.h" -#include "util.h" - -struct reply_callback { - sd_bus_message_handler_t callback; - usec_t timeout; - uint64_t cookie; - unsigned prioq_idx; -}; - -struct filter_callback { - sd_bus_message_handler_t callback; - - unsigned last_iteration; - - LIST_FIELDS(struct filter_callback, callbacks); -}; - -struct match_callback { - sd_bus_message_handler_t callback; - - uint64_t cookie; - unsigned last_iteration; - - char *match_string; - - struct bus_match_node *match_node; -}; - -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); - LIST_HEAD(struct node_object_manager, object_managers); -}; - -struct node_callback { - struct node *node; - - bool is_fallback; - sd_bus_message_handler_t callback; - - unsigned last_iteration; - - LIST_FIELDS(struct node_callback, callbacks); -}; - -struct node_enumerator { - struct node *node; - - sd_bus_node_enumerator_t callback; - - unsigned last_iteration; - - LIST_FIELDS(struct node_enumerator, enumerators); -}; - -struct node_object_manager { - struct node *node; - - LIST_FIELDS(struct node_object_manager, object_managers); -}; - -struct node_vtable { - struct node *node; - - char *interface; - bool is_fallback; - const sd_bus_vtable *vtable; - 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; -}; - -typedef enum BusSlotType { - BUS_REPLY_CALLBACK, - BUS_FILTER_CALLBACK, - BUS_MATCH_CALLBACK, - BUS_NODE_CALLBACK, - BUS_NODE_ENUMERATOR, - BUS_NODE_VTABLE, - BUS_NODE_OBJECT_MANAGER, - _BUS_SLOT_INVALID = -1, -} BusSlotType; - -struct sd_bus_slot { - unsigned n_ref; - sd_bus *bus; - void *userdata; - BusSlotType type:5; - bool floating:1; - bool match_added:1; - char *description; - - LIST_FIELDS(sd_bus_slot, slots); - - union { - struct reply_callback reply_callback; - struct filter_callback filter_callback; - struct match_callback match_callback; - struct node_callback node_callback; - struct node_enumerator node_enumerator; - struct node_object_manager node_object_manager; - struct node_vtable node_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 fake_pids_valid:1; - bool manual_peer_interface:1; - bool is_system:1; - bool is_user:1; - bool allow_interactive_authorization: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; - OrderedHashmap *reply_callbacks; - LIST_HEAD(struct filter_callback, filter_callbacks); - - Hashmap *nodes; - Hashmap *vtable_methods; - Hashmap *vtable_properties; - - union sockaddr_union sockaddr; - socklen_t sockaddr_size; - - char *kernel; - char *machine; - pid_t nspid; - - 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; - - uint64_t creds_mask; - - int *fds; - unsigned n_fds; - - char *exec_path; - char **exec_argv; - - 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_message; - sd_bus_slot *current_slot; - sd_bus_message_handler_t current_handler; - void *current_userdata; - - sd_bus **default_bus_ptr; - pid_t tid; - - struct kdbus_creds fake_creds; - struct kdbus_pids fake_pids; - char *fake_label; - - char *cgroup_root; - - char *description; - - size_t bloom_size; - unsigned bloom_n_hash; - - sd_bus_track *track_queue; - - LIST_HEAD(sd_bus_slot, slots); -}; - -#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_; -char* service_name_startswith(const char *a, const char *b); -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_(sd_bus_unrefp) _unused_ sd_bus *_dont_destroy_##bus = sd_bus_ref(bus) - -int bus_set_address_system(sd_bus *bus); -int bus_set_address_user(sd_bus *bus); -int bus_set_address_system_remote(sd_bus *b, const char *host); -int bus_set_address_system_machine(sd_bus *b, const char *machine); - -int bus_remove_match_by_string(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata); - -int bus_get_root_path(sd_bus *bus); - -int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error); - -#define bus_assert_return(expr, r, error) \ - do { \ - if (!assert_log(expr, #expr)) \ - return sd_bus_error_set_errno(error, r); \ - } while (false) diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c deleted file mode 100644 index 8f93edb8da..0000000000 --- a/src/libsystemd/sd-bus/bus-introspect.c +++ /dev/null @@ -1,212 +0,0 @@ -/*** - 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 . -***/ - -#include "bus-internal.h" -#include "bus-introspect.h" -#include "bus-protocol.h" -#include "bus-signature.h" -#include "fd-util.h" -#include "fileio.h" -#include "string-util.h" -#include "util.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 - "\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, " \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(" \n", i->f); - - if (type == _SD_BUS_VTABLE_METHOD && (flags & SD_BUS_VTABLE_METHOD_NO_REPLY)) - fputs(" \n", i->f); - - if (type == _SD_BUS_VTABLE_PROPERTY || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) { - if (flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT) - fputs(" \n", i->f); - - if (flags & SD_BUS_VTABLE_PROPERTY_CONST) - fputs(" \n", i->f); - else if (flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) - fputs(" \n", i->f); - else if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) - fputs(" \n", i->f); - } - - if (!i->trusted && - (type == _SD_BUS_VTABLE_METHOD || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) && - !(flags & SD_BUS_VTABLE_UNPRIVILEGED)) - fputs(" \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, " 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(" \n", i->f); - break; - - case _SD_BUS_VTABLE_METHOD: - fprintf(i->f, " \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(" \n", i->f); - break; - - case _SD_BUS_VTABLE_PROPERTY: - case _SD_BUS_VTABLE_WRITABLE_PROPERTY: - fprintf(i->f, " \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(" \n", i->f); - break; - - case _SD_BUS_VTABLE_SIGNAL: - fprintf(i->f, " \n", v->x.signal.member); - introspect_write_arguments(i, strempty(v->x.signal.signature), NULL); - introspect_write_flags(i, v->type, v->flags); - fputs(" \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("\n", i->f); - - r = fflush_and_check(i->f); - if (r < 0) - return r; - - 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); - - safe_fclose(i->f); - - free(i->introspection); - zero(*i); -} diff --git a/src/libsystemd/sd-bus/bus-introspect.h b/src/libsystemd/sd-bus/bus-introspect.h deleted file mode 100644 index 8e2f3800ca..0000000000 --- a/src/libsystemd/sd-bus/bus-introspect.h +++ /dev/null @@ -1,40 +0,0 @@ -#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 . -***/ - -#include - -#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 deleted file mode 100644 index 59398b841d..0000000000 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ /dev/null @@ -1,1782 +0,0 @@ -/*** - 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 . -***/ - -#ifdef HAVE_VALGRIND_MEMCHECK_H -#include -#endif - -#include -#include -#include -#include - -/* When we include libgen.h because we need dirname() we immediately - * undefine basename() since libgen.h defines it as a macro to the POSIX - * version which is really broken. We prefer GNU basename(). */ -#include -#undef basename - -#include "alloc-util.h" -#include "bus-bloom.h" -#include "bus-internal.h" -#include "bus-kernel.h" -#include "bus-label.h" -#include "bus-message.h" -#include "bus-util.h" -#include "capability-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "memfd-util.h" -#include "parse-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" -#include "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 start, 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.start = start; - (*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 struct kdbus_bloom_filter *append_bloom(struct kdbus_item **d, size_t length) { - struct kdbus_item *i; - - assert(d); - - i = ALIGN8_PTR(*d); - - i->size = offsetof(struct kdbus_item, bloom_filter) + - offsetof(struct kdbus_bloom_filter, data) + - length; - i->type = KDBUS_ITEM_BLOOM_FILTER; - - *d = (struct kdbus_item *) ((uint8_t*) i + i->size); - - return &i->bloom_filter; -} - -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 void add_bloom_arg(void *data, size_t size, unsigned n_hash, unsigned i, const char *t) { - char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")]; - char *e; - - assert(data); - assert(size > 0); - assert(i < 64); - assert(t); - - 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(data, size, n_hash, buf, t); - - strcpy(e, "-dot-prefix"); - bloom_add_prefixes(data, size, n_hash, buf, t, '.'); - strcpy(e, "-slash-prefix"); - bloom_add_prefixes(data, size, n_hash, buf, t, '/'); -} - -static void add_bloom_arg_has(void *data, size_t size, unsigned n_hash, unsigned i, const char *t) { - char buf[sizeof("arg")-1 + 2 + sizeof("-has")]; - char *e; - - assert(data); - assert(size > 0); - assert(i < 64); - assert(t); - - e = stpcpy(buf, "arg"); - if (i < 10) - *(e++) = '0' + (char) i; - else { - *(e++) = '0' + (char) (i / 10); - *(e++) = '0' + (char) (i % 10); - } - - strcpy(e, "-has"); - bloom_add_pair(data, size, n_hash, buf, t); -} - -static int bus_message_setup_bloom(sd_bus_message *m, struct kdbus_bloom_filter *bloom) { - void *data; - unsigned i; - int r; - - assert(m); - assert(bloom); - - data = bloom->data; - memzero(data, m->bus->bloom_size); - bloom->generation = 0; - - bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "message-type", bus_message_type_to_string(m->header->type)); - - if (m->interface) - bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "interface", m->interface); - if (m->member) - bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "member", m->member); - if (m->path) { - bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path", m->path); - bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path-slash-prefix", m->path); - bloom_add_prefixes(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path-slash-prefix", m->path, '/'); - } - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - for (i = 0; i < 64; i++) { - const char *t, *contents; - char type; - - r = sd_bus_message_peek_type(m, &type, &contents); - if (r < 0) - return r; - - if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) { - - /* The bloom filter includes simple strings of any kind */ - r = sd_bus_message_read_basic(m, type, &t); - if (r < 0) - return r; - - add_bloom_arg(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t); - } - - if (type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g")) { - - /* As well as array of simple strings of any kinds */ - r = sd_bus_message_enter_container(m, type, contents); - if (r < 0) - return r; - - while ((r = sd_bus_message_read_basic(m, contents[0], &t)) > 0) - add_bloom_arg_has(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - } else - /* Stop adding to bloom filter as soon as we - * run into the first argument we cannot add - * to it. */ - break; - } - - return 0; -} - -static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { - struct bus_body_part *part; - struct kdbus_item *d; - const char *destination; - bool well_known = false; - uint64_t dst_id; - size_t sz, dl; - unsigned i; - int r; - - assert(b); - assert(m); - assert(m->sealed); - - /* We put this together only once, if this message is reused - * we reuse the earlier-built version */ - if (m->kdbus) - return 0; - - destination = m->destination ?: m->destination_ptr; - - if (destination) { - r = bus_kernel_parse_unique_name(destination, &dst_id); - if (r < 0) - return r; - if (r == 0) { - well_known = true; - - /* verify_destination_id will usually be 0, which makes the kernel - * driver only look at the provided well-known name. Otherwise, - * the kernel will make sure the provided destination id matches - * the owner of the provided well-known-name, and fail if they - * differ. Currently, this is only needed for bus-proxyd. */ - dst_id = m->verify_destination_id; - } - } else - dst_id = KDBUS_DST_ID_BROADCAST; - - sz = offsetof(struct kdbus_msg, items); - - /* Add in fixed header, fields header and payload */ - sz += (1 + m->n_body_parts) * ALIGN8(offsetof(struct kdbus_item, vec) + - MAX(sizeof(struct kdbus_vec), - sizeof(struct kdbus_memfd))); - - /* Add space for bloom filter */ - sz += ALIGN8(offsetof(struct kdbus_item, bloom_filter) + - offsetof(struct kdbus_bloom_filter, data) + - m->bus->bloom_size); - - /* Add in well-known destination header */ - if (well_known) { - dl = strlen(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; - memzero(m->kdbus, sz); - - m->kdbus->flags = - ((m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_EXPECT_REPLY) | - ((m->header->flags & BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_NO_AUTO_START : 0) | - ((m->header->type == SD_BUS_MESSAGE_SIGNAL) ? KDBUS_MSG_SIGNAL : 0); - - m->kdbus->dst_id = dst_id; - m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS; - m->kdbus->cookie = m->header->dbus2.cookie; - m->kdbus->priority = m->priority; - - if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - m->kdbus->cookie_reply = m->reply_cookie; - else { - struct timespec now; - - assert_se(clock_gettime(CLOCK_MONOTONIC_COARSE, &now) == 0); - m->kdbus->timeout_ns = now.tv_sec * NSEC_PER_SEC + now.tv_nsec + - m->timeout * NSEC_PER_USEC; - } - - d = m->kdbus->items; - - if (well_known) - append_destination(&d, 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 && 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->memfd_offset, 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->header->type == SD_BUS_MESSAGE_SIGNAL) { - struct kdbus_bloom_filter *bloom; - - bloom = append_bloom(&d, m->bus->bloom_size); - r = bus_message_setup_bloom(m, bloom); - 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 void unset_memfds(struct sd_bus_message *m) { - struct bus_body_part *part; - unsigned i; - - assert(m); - - /* Make sure the memfds are not freed twice */ - MESSAGE_FOREACH_PART(part, i, m) - if (part->memfd >= 0) - part->memfd = -1; -} - -static void message_set_timestamp(sd_bus *bus, sd_bus_message *m, const struct kdbus_timestamp *ts) { - assert(bus); - assert(m); - - if (!ts) - return; - - if (!(bus->attach_flags & KDBUS_ATTACH_TIMESTAMP)) - return; - - m->realtime = ts->realtime_ns / NSEC_PER_USEC; - m->monotonic = ts->monotonic_ns / NSEC_PER_USEC; - m->seqnum = ts->seqnum; -} - -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 *header = NULL; - void *footer = NULL; - size_t header_size = 0, footer_size = 0; - size_t n_bytes = 0, idx = 0; - const char *destination = NULL, *seclabel = NULL; - bool last_was_memfd = false; - 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 (!header) { - header = (struct bus_header*)((uint8_t*) k + d->vec.offset); - header_size = d->vec.size; - } - - footer = (uint8_t*) k + d->vec.offset; - footer_size = d->vec.size; - - n_bytes += d->vec.size; - last_was_memfd = false; - break; - - case KDBUS_ITEM_PAYLOAD_MEMFD: - if (!header) /* memfd cannot be first part */ - return -EBADMSG; - - n_bytes += d->memfd.size; - last_was_memfd = true; - 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 (last_was_memfd) /* memfd cannot be last part */ - return -EBADMSG; - - if (!header) - return -EBADMSG; - - if (header_size < sizeof(struct bus_header)) - return -EBADMSG; - - /* on kdbus we only speak native endian gvariant, never dbus1 - * marshalling or reverse endian */ - if (header->version != 2 || - header->endian != BUS_NATIVE_ENDIAN) - return -EPROTOTYPE; - - r = bus_message_from_header( - bus, - header, header_size, - footer, footer_size, - n_bytes, - fds, n_fds, - 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->memfd_offset = d->memfd.start; - part->size = d->memfd.size; - part->sealed = true; - - idx += d->memfd.size; - break; - } - - case KDBUS_ITEM_PIDS: - - /* The PID/TID might be missing, when the data - * is faked by a bus proxy and it lacks that - * information about the real client (since - * SO_PEERCRED is used for that). Also kernel - * namespacing might make some of this data - * unavailable when untranslatable. */ - - if (d->pids.pid > 0) { - m->creds.pid = (pid_t) d->pids.pid; - m->creds.mask |= SD_BUS_CREDS_PID & bus->creds_mask; - } - - if (d->pids.tid > 0) { - m->creds.tid = (pid_t) d->pids.tid; - m->creds.mask |= SD_BUS_CREDS_TID & bus->creds_mask; - } - - if (d->pids.ppid > 0) { - m->creds.ppid = (pid_t) d->pids.ppid; - m->creds.mask |= SD_BUS_CREDS_PPID & bus->creds_mask; - } else if (d->pids.pid == 1) { - m->creds.ppid = 0; - m->creds.mask |= SD_BUS_CREDS_PPID & bus->creds_mask; - } - - break; - - case KDBUS_ITEM_CREDS: - - /* EUID/SUID/FSUID/EGID/SGID/FSGID might be - * missing too (see above). */ - - if ((uid_t) d->creds.uid != UID_INVALID) { - m->creds.uid = (uid_t) d->creds.uid; - m->creds.mask |= SD_BUS_CREDS_UID & bus->creds_mask; - } - - if ((uid_t) d->creds.euid != UID_INVALID) { - m->creds.euid = (uid_t) d->creds.euid; - m->creds.mask |= SD_BUS_CREDS_EUID & bus->creds_mask; - } - - if ((uid_t) d->creds.suid != UID_INVALID) { - m->creds.suid = (uid_t) d->creds.suid; - m->creds.mask |= SD_BUS_CREDS_SUID & bus->creds_mask; - } - - if ((uid_t) d->creds.fsuid != UID_INVALID) { - m->creds.fsuid = (uid_t) d->creds.fsuid; - m->creds.mask |= SD_BUS_CREDS_FSUID & bus->creds_mask; - } - - if ((gid_t) d->creds.gid != GID_INVALID) { - m->creds.gid = (gid_t) d->creds.gid; - m->creds.mask |= SD_BUS_CREDS_GID & bus->creds_mask; - } - - if ((gid_t) d->creds.egid != GID_INVALID) { - m->creds.egid = (gid_t) d->creds.egid; - m->creds.mask |= SD_BUS_CREDS_EGID & bus->creds_mask; - } - - if ((gid_t) d->creds.sgid != GID_INVALID) { - m->creds.sgid = (gid_t) d->creds.sgid; - m->creds.mask |= SD_BUS_CREDS_SGID & bus->creds_mask; - } - - if ((gid_t) d->creds.fsgid != GID_INVALID) { - m->creds.fsgid = (gid_t) d->creds.fsgid; - m->creds.mask |= SD_BUS_CREDS_FSGID & bus->creds_mask; - } - - break; - - case KDBUS_ITEM_TIMESTAMP: - message_set_timestamp(bus, m, &d->timestamp); - 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; - - r = bus_get_root_path(bus); - 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.mask |= SD_BUS_CREDS_AUDIT_SESSION_ID & bus->creds_mask; - - m->creds.audit_login_uid = (uid_t) d->audit.loginuid; - m->creds.mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID & bus->creds_mask; - break; - - case KDBUS_ITEM_CAPS: - if (d->caps.last_cap != cap_last_cap() || - d->size - offsetof(struct kdbus_item, caps.caps) < DIV_ROUND_UP(d->caps.last_cap, 32U) * 4 * 4) { - r = -EBADMSG; - goto fail; - } - - m->creds.capability = d->caps.caps; - 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)) { - r = -EBADMSG; - goto fail; - } - - destination = d->str; - break; - - case KDBUS_ITEM_OWNED_NAME: - if (!service_name_is_valid(d->name.name)) { - r = -EBADMSG; - goto fail; - } - - if (bus->creds_mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { - char **wkn; - size_t n; - - /* We just extend the array here, but - * do not allocate the strings inside - * of it, instead we just point to our - * buffer directly. */ - n = strv_length(m->creds.well_known_names); - wkn = realloc(m->creds.well_known_names, (n + 2) * sizeof(char*)); - if (!wkn) { - r = -ENOMEM; - goto fail; - } - - wkn[n] = d->name.name; - wkn[n+1] = NULL; - m->creds.well_known_names = wkn; - - m->creds.mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; - } - break; - - case KDBUS_ITEM_CONN_DESCRIPTION: - m->creds.description = d->str; - m->creds.mask |= SD_BUS_CREDS_DESCRIPTION & bus->creds_mask; - break; - - case KDBUS_ITEM_AUXGROUPS: - - if (bus->creds_mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - size_t i, n; - gid_t *g; - - n = (d->size - offsetof(struct kdbus_item, data64)) / sizeof(uint64_t); - g = new(gid_t, n); - if (!g) { - r = -ENOMEM; - goto fail; - } - - for (i = 0; i < n; i++) - g[i] = d->data64[i]; - - m->creds.supplementary_gids = g; - m->creds.n_supplementary_gids = n; - m->creds.mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; - } - - break; - - case KDBUS_ITEM_FDS: - case KDBUS_ITEM_SECLABEL: - case KDBUS_ITEM_BLOOM_FILTER: - break; - - default: - log_debug("Got unknown field from kernel %llu", d->type); - } - } - - /* If we requested the list of well-known names to be appended - * and the sender had none no item for it will be - * attached. However, this does *not* mean that the kernel - * didn't want to provide this information to us. Hence, let's - * explicitly mark this information as available if it was - * requested. */ - m->creds.mask |= bus->creds_mask & SD_BUS_CREDS_WELL_KNOWN_NAMES; - - r = bus_message_parse_fields(m); - if (r < 0) - goto fail; - - /* Refuse messages if kdbus and dbus1 cookie doesn't match up */ - if ((uint64_t) m->header->dbus2.cookie != k->cookie) { - r = -EBADMSG; - goto fail; - } - - /* Refuse messages where the reply flag doesn't match up */ - if (!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) != !!(k->flags & KDBUS_MSG_EXPECT_REPLY)) { - r = -EBADMSG; - goto fail; - } - - /* Refuse reply messages where the reply cookie doesn't match up */ - if ((m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) && m->reply_cookie != k->cookie_reply) { - r = -EBADMSG; - goto fail; - } - - /* Refuse messages where the autostart flag doesn't match up */ - if (!(m->header->flags & BUS_MESSAGE_NO_AUTO_START) != !(k->flags & KDBUS_MSG_NO_AUTO_START)) { - r = -EBADMSG; - goto fail; - } - - /* Override information from the user header with data from the kernel */ - if (k->src_id == KDBUS_SRC_ID_KERNEL) - bus_message_set_sender_driver(bus, m); - else { - xsprintf(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 { - xsprintf(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: - unset_memfds(m); - sd_bus_message_unref(m); - - return r; -} - -int bus_kernel_take_fd(sd_bus *b) { - struct kdbus_bloom_parameter *bloom = NULL; - struct kdbus_item *items, *item; - struct kdbus_cmd_hello *hello; - _cleanup_free_ char *g = NULL; - const char *name; - size_t l = 0, m = 0, sz; - int r; - - assert(b); - - if (b->is_server) - return -EINVAL; - - b->use_memfd = 1; - - if (b->description) { - g = bus_label_escape(b->description); - if (!g) - return -ENOMEM; - - name = g; - } else { - char pr[17] = {}; - - /* If no name is explicitly set, we'll include a hint - * indicating the library implementation, a hint which - * kind of bus this is and the thread name */ - - assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0); - - if (isempty(pr)) { - name = b->is_system ? "sd-system" : - b->is_user ? "sd-user" : "sd"; - } else { - _cleanup_free_ char *e = NULL; - - e = bus_label_escape(pr); - if (!e) - return -ENOMEM; - - g = strappend(b->is_system ? "sd-system-" : - b->is_user ? "sd-user-" : "sd-", - e); - if (!g) - return -ENOMEM; - - name = g; - } - - b->description = bus_label_unescape(name); - if (!b->description) - return -ENOMEM; - } - - m = strlen(name); - - sz = ALIGN8(offsetof(struct kdbus_cmd_hello, items)) + - ALIGN8(offsetof(struct kdbus_item, str) + m + 1); - - if (b->fake_creds_valid) - sz += ALIGN8(offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds)); - - if (b->fake_pids_valid) - sz += ALIGN8(offsetof(struct kdbus_item, pids) + sizeof(struct kdbus_pids)); - - if (b->fake_label) { - l = strlen(b->fake_label); - sz += ALIGN8(offsetof(struct kdbus_item, str) + l + 1); - } - - hello = alloca0_align(sz, 8); - hello->size = sz; - hello->flags = b->hello_flags; - hello->attach_flags_send = _KDBUS_ATTACH_ANY; - hello->attach_flags_recv = b->attach_flags; - hello->pool_size = KDBUS_POOL_SIZE; - - item = hello->items; - - item->size = offsetof(struct kdbus_item, str) + m + 1; - item->type = KDBUS_ITEM_CONN_DESCRIPTION; - memcpy(item->str, name, m + 1); - item = KDBUS_ITEM_NEXT(item); - - 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_pids_valid) { - item->size = offsetof(struct kdbus_item, pids) + sizeof(struct kdbus_pids); - item->type = KDBUS_ITEM_PIDS; - item->pids = b->fake_pids; - - item = KDBUS_ITEM_NEXT(item); - } - - if (b->fake_label) { - item->size = offsetof(struct kdbus_item, str) + l + 1; - item->type = KDBUS_ITEM_SECLABEL; - memcpy(item->str, b->fake_label, l+1); - } - - r = ioctl(b->input_fd, KDBUS_CMD_HELLO, hello); - if (r < 0) { - if (errno == ENOTTY) - /* If the ioctl is not supported we assume that the - * API version changed in a major incompatible way, - * let's indicate an API incompatibility in this - * case. */ - return -ESOCKTNOSUPPORT; - - 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; - r = -errno; - goto fail; - } - } - - /* The higher 32bit of the bus_flags fields are considered - * 'incompatible flags'. Refuse them all for now. */ - if (hello->bus_flags > 0xFFFFFFFFULL) { - r = -ESOCKTNOSUPPORT; - goto fail; - } - - /* extract bloom parameters from items */ - items = (void*)((uint8_t*)b->kdbus_buffer + hello->offset); - KDBUS_FOREACH(item, items, hello->items_size) { - switch (item->type) { - case KDBUS_ITEM_BLOOM_PARAMETER: - bloom = &item->bloom_parameter; - break; - } - } - - if (!bloom || !bloom_validate_parameters((size_t) bloom->size, (unsigned) bloom->n_hash)) { - r = -EOPNOTSUPP; - goto fail; - } - - b->bloom_size = (size_t) bloom->size; - b->bloom_n_hash = (unsigned) bloom->n_hash; - - if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello->id) < 0) { - r = -ENOMEM; - goto fail; - } - - b->unique_id = hello->id; - - b->is_kernel = true; - b->bus_client = true; - b->can_fds = !!(hello->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)); - - /* free returned items */ - (void) bus_kernel_cmd_free(b, hello->offset); - return bus_start_running(b); - -fail: - (void) bus_kernel_cmd_free(b, hello->offset); - return r; -} - -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_cmd_free(sd_bus *bus, uint64_t offset) { - struct kdbus_cmd_free cmd = { - .size = sizeof(cmd), - .offset = offset, - }; - int r; - - assert(bus); - assert(bus->is_kernel); - - r = ioctl(bus->input_fd, KDBUS_CMD_FREE, &cmd); - if (r < 0) - return -errno; - - return 0; -} - -static void close_kdbus_msg(sd_bus *bus, struct kdbus_msg *k) { - struct kdbus_item *d; - - assert(bus); - assert(k); - - 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) - safe_close(d->memfd.fd); - } - - bus_kernel_cmd_free(bus, (uint8_t*) k - (uint8_t*) bus->kdbus_buffer); -} - -int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call) { - struct kdbus_cmd_send cmd = { }; - 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; - - cmd.size = sizeof(cmd); - cmd.msg_address = (uintptr_t)m->kdbus; - - /* If this is a synchronous method call, then let's tell the - * kernel, so that it can pass CPU time/scheduling to the - * destination for the time, if it wants to. If we - * synchronously wait for the result anyway, we won't need CPU - * anyway. */ - if (hint_sync_call) { - m->kdbus->flags |= KDBUS_MSG_EXPECT_REPLY; - cmd.flags |= KDBUS_SEND_SYNC_REPLY; - } - - r = ioctl(bus->output_fd, KDBUS_CMD_SEND, &cmd); - if (r < 0) { - _cleanup_(sd_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; - - } else if (hint_sync_call) { - struct kdbus_msg *k; - - k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + cmd.reply.offset); - assert(k); - - if (k->payload_type == KDBUS_PAYLOAD_DBUS) { - - r = bus_kernel_make_message(bus, k); - if (r < 0) { - close_kdbus_msg(bus, k); - - /* Anybody can send us invalid messages, let's just drop them. */ - if (r == -EBADMSG || r == -EPROTOTYPE) - log_debug_errno(r, "Ignoring invalid synchronous reply: %m"); - else - return r; - } - } else { - log_debug("Ignoring message with unknown payload type %llu.", (unsigned long long) k->payload_type); - close_kdbus_msg(bus, k); - } - } - - return 1; -} - -static int push_name_owner_changed( - sd_bus *bus, - const char *name, - const char *old_owner, - const char *new_owner, - const struct kdbus_timestamp *ts) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert(bus); - - r = sd_bus_message_new_signal( - bus, - &m, - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "NameOwnerChanged"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "sss", name, old_owner, new_owner); - if (r < 0) - return r; - - bus_message_set_sender_driver(bus, m); - message_set_timestamp(bus, m, ts); - - 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, - const struct kdbus_msg *k, - const struct kdbus_item *d, - const struct kdbus_timestamp *ts) { - - 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_id.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.id); - - if (d->type == KDBUS_ITEM_NAME_REMOVE || (d->name_change.new_id.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.id); - - return push_name_owner_changed(bus, d->name_change.name, old_owner, new_owner, ts); -} - -static int translate_id_change( - sd_bus *bus, - const struct kdbus_msg *k, - const struct kdbus_item *d, - const struct kdbus_timestamp *ts) { - - 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, - ts); -} - -static int translate_reply( - sd_bus *bus, - const struct kdbus_msg *k, - const struct kdbus_item *d, - const struct kdbus_timestamp *ts) { - - _cleanup_(sd_bus_message_unrefp) 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; - - message_set_timestamp(bus, m, ts); - - 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) { - static int (* const translate[])(sd_bus *bus, const struct kdbus_msg *k, const struct kdbus_item *d, const struct kdbus_timestamp *ts) = { - [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, - }; - - struct kdbus_item *d, *found = NULL; - struct kdbus_timestamp *ts = NULL; - - assert(bus); - assert(k); - assert(k->payload_type == KDBUS_PAYLOAD_KERNEL); - - KDBUS_ITEM_FOREACH(d, k, items) { - if (d->type == KDBUS_ITEM_TIMESTAMP) - ts = &d->timestamp; - else 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, ts); -} - -int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority) { - struct kdbus_cmd_recv recv = { .size = sizeof(recv) }; - struct kdbus_msg *k; - int r; - - assert(bus); - - r = bus_rqueue_make_room(bus); - if (r < 0) - return r; - - if (hint_priority) { - recv.flags |= KDBUS_RECV_USE_PRIORITY; - recv.priority = priority; - } - - r = ioctl(bus->input_fd, KDBUS_CMD_RECV, &recv); - if (recv.return_flags & KDBUS_RECV_RETURN_DROPPED_MSGS) - log_debug("%s: kdbus reports %" PRIu64 " dropped broadcast messages, ignoring.", strna(bus->description), (uint64_t) recv.dropped_msgs); - if (r < 0) { - if (errno == EAGAIN) - return 0; - - return -errno; - } - - k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + recv.msg.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_errno(r, "Ignoring invalid message: %m"); - r = 0; - } - - if (r <= 0) - close_kdbus_msg(bus, k); - } else if (k->payload_type == KDBUS_PAYLOAD_KERNEL) { - r = bus_kernel_translate_message(bus, k); - close_kdbus_msg(bus, k); - } else { - log_debug("Ignoring message with unknown payload type %llu.", (unsigned long long) k->payload_type); - 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 -EOPNOTSUPP; - - assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) == 0); - - if (bus->n_memfd_cache <= 0) { - int r; - - assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) == 0); - - r = memfd_new(bus->description); - if (r < 0) - return r; - - *address = NULL; - *mapped = 0; - *allocated = 0; - return r; - } - - 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); - - safe_close(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(memfd_set_size(fd, 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); -} - -uint64_t request_name_flags_to_kdbus(uint64_t flags) { - uint64_t f = 0; - - 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; - - return f; -} - -uint64_t attach_flags_to_kdbus(uint64_t mask) { - uint64_t m = 0; - - if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) - m |= KDBUS_ATTACH_CREDS; - - if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID)) - m |= KDBUS_ATTACH_PIDS; - - if (mask & SD_BUS_CREDS_COMM) - m |= KDBUS_ATTACH_PID_COMM; - - if (mask & SD_BUS_CREDS_TID_COMM) - m |= KDBUS_ATTACH_TID_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; - - if (mask & SD_BUS_CREDS_DESCRIPTION) - m |= KDBUS_ATTACH_CONN_DESCRIPTION; - - if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) - m |= KDBUS_ATTACH_AUXGROUPS; - - return m; -} - -int bus_kernel_create_bus(const char *name, bool world, char **s) { - struct kdbus_cmd *make; - struct kdbus_item *n; - size_t l; - int fd; - - assert(name); - assert(s); - - fd = open("/sys/fs/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - l = strlen(name); - make = alloca0_align(offsetof(struct kdbus_cmd, items) + - ALIGN8(offsetof(struct kdbus_item, bloom_parameter) + sizeof(struct kdbus_bloom_parameter)) + - ALIGN8(offsetof(struct kdbus_item, data64) + sizeof(uint64_t)) + - ALIGN8(offsetof(struct kdbus_item, str) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1), - 8); - - make->size = offsetof(struct kdbus_cmd, items); - - /* Set the bloom parameters */ - n = make->items; - n->size = offsetof(struct kdbus_item, bloom_parameter) + - sizeof(struct kdbus_bloom_parameter); - n->type = KDBUS_ITEM_BLOOM_PARAMETER; - n->bloom_parameter.size = DEFAULT_BLOOM_SIZE; - n->bloom_parameter.n_hash = DEFAULT_BLOOM_N_HASH; - - assert_cc(DEFAULT_BLOOM_SIZE > 0); - assert_cc(DEFAULT_BLOOM_N_HASH > 0); - - make->size += ALIGN8(n->size); - - /* Provide all metadata via bus-owner queries */ - n = KDBUS_ITEM_NEXT(n); - n->type = KDBUS_ITEM_ATTACH_FLAGS_SEND; - n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t); - n->data64[0] = _KDBUS_ATTACH_ANY; - make->size += ALIGN8(n->size); - - /* Set the a good name */ - n = KDBUS_ITEM_NEXT(n); - sprintf(n->str, UID_FMT "-%s", 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 = world ? KDBUS_MAKE_ACCESS_WORLD : 0; - - if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) { - safe_close(fd); - - /* Major API change? then the ioctls got shuffled around. */ - if (errno == ENOTTY) - return -ESOCKTNOSUPPORT; - - return -errno; - } - - if (s) { - char *p; - - p = strjoin("/sys/fs/kdbus/", n->str, "/bus", NULL); - if (!p) { - safe_close(fd); - return -ENOMEM; - } - - *s = p; - } - - return fd; -} - -int bus_kernel_open_bus_fd(const char *bus, char **path) { - char *p; - int fd; - size_t len; - - assert(bus); - - len = strlen("/sys/fs/kdbus/") + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + strlen("/bus") + 1; - - if (path) { - p = new(char, len); - if (!p) - return -ENOMEM; - } else - p = newa(char, len); - - sprintf(p, "/sys/fs/kdbus/" UID_FMT "-%s/bus", getuid(), bus); - - fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) { - if (path) - free(p); - - return -errno; - } - - if (path) - *path = p; - - return fd; -} - -int bus_kernel_try_close(sd_bus *bus) { - struct kdbus_cmd byebye = { .size = sizeof(byebye) }; - - assert(bus); - assert(bus->is_kernel); - - if (ioctl(bus->input_fd, KDBUS_CMD_BYEBYE, &byebye) < 0) - return -errno; - - return 0; -} - -int bus_kernel_drop_one(int fd) { - struct kdbus_cmd_recv recv = { - .size = sizeof(recv), - .flags = KDBUS_RECV_DROP, - }; - - assert(fd >= 0); - - if (ioctl(fd, KDBUS_CMD_RECV, &recv) < 0) - return -errno; - - return 0; -} - -int bus_kernel_realize_attach_flags(sd_bus *bus) { - struct kdbus_cmd *update; - struct kdbus_item *n; - - assert(bus); - assert(bus->is_kernel); - - update = alloca0_align(offsetof(struct kdbus_cmd, items) + - ALIGN8(offsetof(struct kdbus_item, data64) + sizeof(uint64_t)), - 8); - - n = update->items; - n->type = KDBUS_ITEM_ATTACH_FLAGS_RECV; - n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t); - n->data64[0] = bus->attach_flags; - - update->size = - offsetof(struct kdbus_cmd, items) + - ALIGN8(n->size); - - if (ioctl(bus->input_fd, KDBUS_CMD_UPDATE, update) < 0) - return -errno; - - return 0; -} - -int bus_kernel_get_bus_name(sd_bus *bus, char **name) { - struct kdbus_cmd_info cmd = { - .size = sizeof(struct kdbus_cmd_info), - }; - struct kdbus_info *info; - struct kdbus_item *item; - char *n = NULL; - int r; - - assert(bus); - assert(name); - assert(bus->is_kernel); - - r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); - if (r < 0) - return -errno; - - info = (struct kdbus_info*) ((uint8_t*) bus->kdbus_buffer + cmd.offset); - - KDBUS_ITEM_FOREACH(item, info, items) - if (item->type == KDBUS_ITEM_MAKE_NAME) { - r = free_and_strdup(&n, item->str); - break; - } - - bus_kernel_cmd_free(bus, cmd.offset); - - if (r < 0) - return r; - if (!n) - return -EIO; - - *name = n; - return 0; -} diff --git a/src/libsystemd/sd-bus/bus-kernel.h b/src/libsystemd/sd-bus/bus-kernel.h deleted file mode 100644 index 53ba3bdcf3..0000000000 --- a/src/libsystemd/sd-bus/bus-kernel.h +++ /dev/null @@ -1,93 +0,0 @@ -#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 . -***/ - -#include - -#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) && \ - ((uint8_t *) part >= (uint8_t *) head); \ - part = KDBUS_ITEM_NEXT(part)) -#define KDBUS_FOREACH(iter, first, _size) \ - for (iter = (first); \ - ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ - ((uint8_t *)(iter) >= (uint8_t *)(first)); \ - iter = (void*)(((uint8_t *)iter) + ALIGN8((iter)->size))) - -#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 (512*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, bool hint_sync_call); -int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority); - -int bus_kernel_open_bus_fd(const char *bus, char **path); - -int bus_kernel_create_bus(const char *name, bool world, char **s); -int bus_kernel_create_endpoint(const char *bus_name, const char *ep_name, char **path); - -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); - -uint64_t request_name_flags_to_kdbus(uint64_t sd_bus_flags); -uint64_t attach_flags_to_kdbus(uint64_t sd_bus_flags); - -int bus_kernel_try_close(sd_bus *bus); - -int bus_kernel_drop_one(int fd); - -int bus_kernel_realize_attach_flags(sd_bus *bus); - -int bus_kernel_get_bus_name(sd_bus *bus, char **name); - -int bus_kernel_cmd_free(sd_bus *bus, uint64_t offset); diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c deleted file mode 100644 index 397baf6f33..0000000000 --- a/src/libsystemd/sd-bus/bus-match.c +++ /dev/null @@ -1,1218 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-match.h" -#include "bus-message.h" -#include "bus-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "hexdecoct.h" -#include "string-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_HAS_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) || - (t >= BUS_MATCH_ARG_HAS && t <= BUS_MATCH_ARG_HAS_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->type == BUS_MATCH_ROOT) - return false; - - 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, - char **value_strv, - 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: - - if (value_str) - return streq_ptr(node->value.str, value_str); - - return false; - - case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: { - char **i; - - STRV_FOREACH(i, value_strv) - if (streq_ptr(node->value.str, *i)) - return true; - - return false; - } - - case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: - if (value_str) - return namespace_simple_pattern(node->value.str, value_str); - - return false; - - case BUS_MATCH_PATH_NAMESPACE: - return path_simple_pattern(node->value.str, value_str); - - case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: - if (value_str) - return path_complex_pattern(node->value.str, value_str); - - return false; - - 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_HAS ... BUS_MATCH_ARG_HAS_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) { - - _cleanup_strv_free_ char **test_strv = NULL; - 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.callback->last_iteration == bus->iteration_counter) - return 0; - - node->leaf.callback->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->callback) { - _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; - sd_bus_slot *slot; - - slot = container_of(node->leaf.callback, sd_bus_slot, match_callback); - if (bus) { - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = node->leaf.callback->callback; - bus->current_userdata = slot->userdata; - } - r = node->leaf.callback->callback(m, slot->userdata, &error_buffer); - if (bus) { - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - } - - r = bus_maybe_reply_error(m, r, &error_buffer); - if (r != 0) - return r; - - if (bus && bus->match_callbacks_modified) - return 0; - } - - 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: - (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str); - break; - - case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: - (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str); - break; - - case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: - (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str); - break; - - case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: - (void) bus_message_get_arg_strv(m, node->type - BUS_MATCH_ARG_HAS, &test_strv); - 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 (test_strv) { - char **i; - - STRV_FOREACH(i, test_strv) { - found = hashmap_get(node->compare.children, *i); - if (found) { - r = bus_match_run(bus, found, m); - if (r != 0) - return r; - } - } - - found = NULL; - } 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, test_strv, 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(NULL); - if (!c->compare.children) { - r = -ENOMEM; - goto fail; - } - } else if (BUS_MATCH_CAN_HASH(t)) { - c->compare.children = hashmap_new(&string_hash_ops); - 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; n && !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, - struct match_callback *callback) { - - struct bus_match_node *n; - - assert(where); - assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE); - assert(callback); - - 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; - callback->match_node = n; - - where->child = 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) { - sd_bus_slot *s; - - s = container_of(c->leaf.callback, sd_bus_slot, match_callback); - - if (c->type == BUS_MATCH_LEAF && - c->leaf.callback->callback == callback && - s->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; - } - - if (n == 7 && startswith(k, "arg") && startswith(k + 4, "has")) { - int j; - - j = undecchar(k[3]); - if (j < 0) - return -EINVAL; - - return BUS_MATCH_ARG_HAS + j; - } - - if (n == 8 && startswith(k, "arg") && startswith(k + 5, "has")) { - 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_HAS + a * 10 + b; - if (t > BUS_MATCH_ARG_HAS_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; - - /* Avahi's match rules appear to include whitespace, skip over it */ - p += strspn(p, " "); - - 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; - - value = mfree(value); - } 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[quoted] == 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_fclose_ FILE *f = NULL; - char *buffer = NULL; - size_t size = 0; - unsigned i; - int r; - - 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); - } - - r = fflush_and_check(f); - if (r < 0) - return NULL; - - return buffer; -} - -int bus_match_add( - struct bus_match_node *root, - struct bus_match_component *components, - unsigned n_components, - struct match_callback *callback) { - - unsigned i; - struct bus_match_node *n; - int r; - - assert(root); - assert(callback); - - 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; - } - - return bus_match_add_leaf(n, callback); -} - -int bus_match_remove( - struct bus_match_node *root, - struct match_callback *callback) { - - struct bus_match_node *node, *pp; - - assert(root); - assert(callback); - - node = callback->match_node; - if (!node) - return 0; - - assert(node->type == BUS_MATCH_LEAF); - - callback->match_node = NULL; - - /* Free the leaf */ - pp = node->parent; - bus_match_node_free(node); - - /* Prune the tree above */ - while (pp) { - node = pp; - pp = node->parent; - - if (!bus_match_node_maybe_free(node)) - break; - } - - return 1; -} - -int bus_match_find( - struct bus_match_node *root, - struct bus_match_component *components, - unsigned n_components, - sd_bus_message_handler_t callback, - void *userdata, - struct match_callback **ret) { - - struct bus_match_node *n, **gc; - unsigned i; - int r; - - assert(root); - assert(ret); - - 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; - - *ret = n->leaf.callback; - return 1; -} - -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; - - case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: - snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS); - 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->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->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); -} - -enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) { - bool found_driver = false; - unsigned i; - - if (n_components <= 0) - return BUS_MATCH_GENERIC; - - assert(components); - - /* Checks whether the specified match can only match the - * pseudo-service for local messages, which we detect by - * sender, interface or path. If a match is not restricted to - * local messages, then we check if it only matches on the - * driver. */ - - for (i = 0; i < n_components; i++) { - const struct bus_match_component *c = components + i; - - if (c->type == BUS_MATCH_SENDER) { - if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local")) - return BUS_MATCH_LOCAL; - - if (streq_ptr(c->value_str, "org.freedesktop.DBus")) - found_driver = true; - } - - if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local")) - return BUS_MATCH_LOCAL; - - if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local")) - return BUS_MATCH_LOCAL; - } - - return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC; - -} diff --git a/src/libsystemd/sd-bus/bus-match.h b/src/libsystemd/sd-bus/bus-match.h deleted file mode 100644 index 8cbbb63b11..0000000000 --- a/src/libsystemd/sd-bus/bus-match.h +++ /dev/null @@ -1,100 +0,0 @@ -#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 . -***/ - -#include "sd-bus.h" - -#include "hashmap.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_ARG_HAS, - BUS_MATCH_ARG_HAS_LAST = BUS_MATCH_ARG_HAS + 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 { - struct match_callback *callback; - } 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; -}; - -enum bus_match_scope { - BUS_MATCH_GENERIC, - BUS_MATCH_LOCAL, - BUS_MATCH_DRIVER, -}; - -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, struct match_callback *callback); -int bus_match_remove(struct bus_match_node *root, struct match_callback *callback); - -int bus_match_find(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, sd_bus_message_handler_t callback, void *userdata, struct match_callback **ret); - -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); - -enum bus_match_scope bus_match_get_scope(const 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 deleted file mode 100644 index b8958ec7bb..0000000000 --- a/src/libsystemd/sd-bus/bus-message.c +++ /dev/null @@ -1,5939 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-gvariant.h" -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-signature.h" -#include "bus-type.h" -#include "bus-util.h" -#include "fd-util.h" -#include "io-util.h" -#include "memfd-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "utf8.h" -#include "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) { - assert(part->memfd_offset == 0); - assert(part->data == part->mmap_begin); - bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped, part->allocated); - } else { - if (part->mapped > 0) - assert_se(munmap(part->mmap_begin, part->mapped) == 0); - - safe_close(part->memfd); - } - - } else if (part->munmap_this) - munmap(part->mmap_begin, 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); - } - - m->containers = mfree(m->containers); - - 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->release_kdbus) - bus_kernel_cmd_free(m->bus, (uint8_t *) m->kdbus - (uint8_t *) m->bus->kdbus_buffer); - - if (m->free_kdbus) - free(m->kdbus); - - 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); - - m->destination_ptr = mfree(m->destination_ptr); - 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->fields_size; - start = ALIGN_TO(old_size, align); - new_size = start + sz; - - if (new_size < start || - new_size > (size_t) ((uint32_t) -1)) - goto poison; - - if (old_size == new_size) - return (uint8_t*) m->header + old_size; - - 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) - memzero((uint8_t*) np + old_size, start - old_size); - - op = m->header; - m->header = np; - m->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, - uint64_t h, - char type, - const char *s, - const char **ret) { - - size_t l; - uint8_t *p; - - assert(m); - - /* dbus1 only allows 8bit header field ids */ - if (h > 0xFF) - return -EINVAL; - - /* 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 64bit, ((string + NUL) + NUL + signature string 's') */ - p = message_extend_fields(m, 8, 8 + l + 1 + 1 + 1, true); - if (!p) - return -ENOMEM; - - *((uint64_t*) p) = h; - 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] = (uint8_t) 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, - uint64_t h, - const char *s, - const char **ret) { - - size_t l; - uint8_t *p; - - assert(m); - - /* dbus1 only allows 8bit header field ids */ - if (h > 0xFF) - return -EINVAL; - - /* dbus1 doesn't allow signatures over 8bit, 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] = (uint8_t) 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, uint64_t h, uint32_t x) { - uint8_t *p; - - assert(m); - - /* dbus1 only allows 8bit header field ids */ - if (h > 0xFF) - return -EINVAL; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - /* (field id 64bit + ((value + NUL + signature string 'u') */ - - p = message_extend_fields(m, 8, 8 + 4 + 1 + 1, true); - if (!p) - return -ENOMEM; - - *((uint64_t*) p) = h; - *((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] = (uint8_t) h; - p[1] = 1; - p[2] = 'u'; - p[3] = 0; - - ((uint32_t*) p)[1] = x; - } - - return 0; -} - -static int message_append_field_uint64(sd_bus_message *m, uint64_t h, uint64_t x) { - uint8_t *p; - - assert(m); - - /* dbus1 only allows 8bit header field ids */ - if (h > 0xFF) - return -EINVAL; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - /* (field id 64bit + ((value + NUL + signature string 't') */ - - p = message_extend_fields(m, 8, 8 + 8 + 1 + 1, true); - if (!p) - return -ENOMEM; - - *((uint64_t*) p) = h; - *((uint64_t*) (p + 8)) = x; - p[16] = 0; - p[17] = 't'; - } else { - /* (field id byte + (signature length + signature 't' + NUL) + 4 byte padding + value) */ - p = message_extend_fields(m, 8, 4 + 4 + 8, false); - if (!p) - return -ENOMEM; - - p[0] = (uint8_t) h; - p[1] = 1; - p[2] = 't'; - p[3] = 0; - p[4] = 0; - p[5] = 0; - p[6] = 0; - p[7] = 0; - - ((uint64_t*) p)[1] = x; - } - - return 0; -} - -static int message_append_reply_cookie(sd_bus_message *m, uint64_t cookie) { - assert(m); - - if (BUS_MESSAGE_IS_GVARIANT(m)) - return message_append_field_uint64(m, BUS_MESSAGE_HEADER_REPLY_SERIAL, cookie); - else { - /* 64bit cookies are not supported on dbus1 */ - if (cookie > 0xffffffffUL) - return -EOPNOTSUPP; - - return message_append_field_uint32(m, BUS_MESSAGE_HEADER_REPLY_SERIAL, (uint32_t) cookie); - } -} - -int bus_message_from_header( - sd_bus *bus, - void *header, - size_t header_accessible, - void *footer, - size_t footer_accessible, - size_t message_size, - int *fds, - unsigned n_fds, - const char *label, - size_t extra, - sd_bus_message **ret) { - - _cleanup_free_ sd_bus_message *m = NULL; - struct bus_header *h; - size_t a, label_sz; - - assert(bus); - assert(header || header_accessible <= 0); - assert(footer || footer_accessible <= 0); - assert(fds || n_fds <= 0); - assert(ret); - - if (header_accessible < sizeof(struct bus_header)) - return -EBADMSG; - - if (header_accessible > message_size) - return -EBADMSG; - if (footer_accessible > message_size) - return -EBADMSG; - - h = header; - if (!IN_SET(h->version, 1, 2)) - return -EBADMSG; - - if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID) - return -EBADMSG; - - if (!IN_SET(h->endian, BUS_LITTLE_ENDIAN, BUS_BIG_ENDIAN)) - return -EBADMSG; - - /* Note that we are happy with unknown flags in the flags header! */ - - 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 = header; - m->header_accessible = header_accessible; - m->footer = footer; - m->footer_accessible = footer_accessible; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - size_t ws; - - if (h->dbus2.cookie == 0) - return -EBADMSG; - - /* dbus2 derives the sizes from the message size and - the offset table at the end, since it is formatted as - gvariant "yyyyuta{tv}v". Since the message itself is a - structure with precisely to variable sized entries, - there's only one offset in the table, which marks the - end of the fields array. */ - - ws = bus_gvariant_determine_word_size(message_size, 0); - if (footer_accessible < ws) - return -EBADMSG; - - m->fields_size = bus_gvariant_read_word_le((uint8_t*) footer + footer_accessible - ws, ws); - if (ALIGN8(m->fields_size) > message_size - ws) - return -EBADMSG; - if (m->fields_size < sizeof(struct bus_header)) - return -EBADMSG; - - m->fields_size -= sizeof(struct bus_header); - m->body_size = message_size - (sizeof(struct bus_header) + ALIGN8(m->fields_size)); - } else { - if (h->dbus1.serial == 0) - return -EBADMSG; - - /* dbus1 has the sizes in the header */ - m->fields_size = BUS_MESSAGE_BSWAP32(m, h->dbus1.fields_size); - m->body_size = BUS_MESSAGE_BSWAP32(m, h->dbus1.body_size); - - if (sizeof(struct bus_header) + ALIGN8(m->fields_size) + m->body_size != message_size) - return -EBADMSG; - } - - m->fds = fds; - m->n_fds = n_fds; - - 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; - } - - m->bus = sd_bus_ref(bus); - *ret = m; - m = NULL; - - return 0; -} - -int bus_message_from_malloc( - sd_bus *bus, - void *buffer, - size_t length, - int *fds, - unsigned n_fds, - const char *label, - sd_bus_message **ret) { - - sd_bus_message *m; - size_t sz; - int r; - - r = bus_message_from_header( - bus, - buffer, length, /* in this case the initial bytes and the final bytes are the same */ - buffer, length, - length, - fds, n_fds, - label, - 0, &m); - if (r < 0) - return r; - - sz = length - sizeof(struct bus_header) - ALIGN8(m->fields_size); - if (sz > 0) { - m->n_body_parts = 1; - m->body.data = (uint8_t*) buffer + sizeof(struct bus_header) + ALIGN8(m->fields_size); - 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; - - assert(bus); - - 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->message_version; - m->allow_fds = bus->can_fds || (bus->state != BUS_HELLO && bus->state != BUS_RUNNING); - m->root_container.need_offsets = BUS_MESSAGE_IS_GVARIANT(m); - m->bus = sd_bus_ref(bus); - - if (bus->allow_interactive_authorization) - m->header->flags |= BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION; - - return m; -} - -_public_ int sd_bus_message_new_signal( - sd_bus *bus, - sd_bus_message **m, - const char *path, - const char *interface, - const char *member) { - - sd_bus_message *t; - int r; - - assert_return(bus, -ENOTCONN); - assert_return(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, - sd_bus_message **m, - const char *destination, - const char *path, - const char *interface, - const char *member) { - - sd_bus_message *t; - int r; - - assert_return(bus, -ENOTCONN); - assert_return(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->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); - if (t->reply_cookie == 0) - return -EOPNOTSUPP; - - r = message_append_reply_cookie(t, 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, - sd_bus_message **m, - const sd_bus_error *e) { - - 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_(sd_bus_error_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, m, &error); -} - -_public_ int sd_bus_message_new_method_errno( - sd_bus_message *call, - sd_bus_message **m, - int error, - const sd_bus_error *p) { - - _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; - - if (sd_bus_error_is_set(p)) - return sd_bus_message_new_method_error(call, m, p); - - sd_bus_error_set_errno(&berror, error); - - return sd_bus_message_new_method_error(call, m, &berror); -} - -_public_ int sd_bus_message_new_method_errnof( - sd_bus_message *call, - sd_bus_message **m, - int error, - const char *format, - ...) { - - _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; - va_list ap; - - va_start(ap, format); - sd_bus_error_set_errnofv(&berror, error, format, ap); - va_end(ap); - - return sd_bus_message_new_method_error(call, m, &berror); -} - -void bus_message_set_sender_local(sd_bus *bus, sd_bus_message *m) { - assert(bus); - assert(m); - - m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus.Local"; - m->creds.well_known_names_local = true; - m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; -} - -void bus_message_set_sender_driver(sd_bus *bus, sd_bus_message *m) { - assert(bus); - assert(m); - - m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus"; - m->creds.well_known_names_driver = true; - m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; -} - -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(bus); - 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_reply_cookie(t, 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; - - bus_message_set_sender_driver(bus, t); - - *m = t; - return 0; - -fail: - message_free(t); - return r; -} - -_public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) { - - if (!m) - return 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) - return NULL; - - 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) { - uint64_t c; - - assert_return(m, -EINVAL); - assert_return(cookie, -EINVAL); - - c = BUS_MESSAGE_COOKIE(m); - if (c == 0) - return -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); - - if (m->reply_cookie == 0) - return -ENODATA; - - *cookie = m->reply_cookie; - return 0; -} - -_public_ int sd_bus_message_get_expect_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); -} - -_public_ int sd_bus_message_get_auto_start(sd_bus_message *m) { - assert_return(m, -EINVAL); - - return !(m->header->flags & BUS_MESSAGE_NO_AUTO_START); -} - -_public_ int sd_bus_message_get_allow_interactive_authorization(sd_bus_message *m) { - assert_return(m, -EINVAL); - - return m->header->type == SD_BUS_MESSAGE_METHOD_CALL && - (m->header->flags & BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION); -} - -_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); - - if (!sd_bus_error_is_set(&m->error)) - return NULL; - - return &m->error; -} - -_public_ int sd_bus_message_get_monotonic_usec(sd_bus_message *m, uint64_t *usec) { - assert_return(m, -EINVAL); - assert_return(usec, -EINVAL); - - if (m->monotonic <= 0) - return -ENODATA; - - *usec = m->monotonic; - return 0; -} - -_public_ int sd_bus_message_get_realtime_usec(sd_bus_message *m, uint64_t *usec) { - assert_return(m, -EINVAL); - assert_return(usec, -EINVAL); - - if (m->realtime <= 0) - return -ENODATA; - - *usec = m->realtime; - return 0; -} - -_public_ int sd_bus_message_get_seqnum(sd_bus_message *m, uint64_t *seqnum) { - assert_return(m, -EINVAL); - assert_return(seqnum, -EINVAL); - - if (m->seqnum <= 0) - return -ENODATA; - - *seqnum = m->seqnum; - 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_expect_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); - - SET_FLAG(m->header->flags, BUS_MESSAGE_NO_REPLY_EXPECTED, !b); - - return 0; -} - -_public_ int sd_bus_message_set_auto_start(sd_bus_message *m, int b) { - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - SET_FLAG(m->header->flags, BUS_MESSAGE_NO_AUTO_START, !b); - - return 0; -} - -_public_ int sd_bus_message_set_allow_interactive_authorization(sd_bus_message *m, int b) { - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - SET_FLAG(m->header->flags, BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION, b); - - 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); - part->mmap_begin = part->data; - } - - 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 = memfd_set_size(part->memfd, new_allocated); - if (r < 0) { - m->poisoned = true; - return r; - } - - 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->mmap_begin, part->mapped, psz, MREMAP_MAYMOVE); - - if (n == MAP_FAILED) { - m->poisoned = true; - return -errno; - } - - part->mmap_begin = part->data = n; - part->mapped = psz; - part->memfd_offset = 0; - } - - 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, - bool force_inline) { - - 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->body_size, align); - end_body = start_body + sz; - - padding = start_body - m->body_size; - added = padding + sz; - - /* Check for 32bit overflows */ - if (end_body > (size_t) ((uint32_t) -1) || - end_body < start_body) { - 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) || - (force_inline && m->body_end->size > MEMFD_MIN_SIZE); /* if this must be an inlined extension, let's create a new part if the previous part is large enough to be inlined */ - - 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) { - memzero(p, 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->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 -EOPNOTSUPP; - - 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; - safe_close(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, false); - 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, 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, false); - if (!a) - return -ENOMEM; - - *s = a; - } else { - a = message_extend_body(m, 4, 4 + size + 1, false, 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, false)) - return -ENOMEM; - - r = bus_gvariant_is_fixed_size(contents); - if (r < 0) - return r; - - *begin = m->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, 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, 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, false)) - return -ENOMEM; - - } else { - size_t l; - void *a; - - l = strlen(contents); - a = message_extend_body(m, 1, 1 + l + 1, false, 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, false)) - return -ENOMEM; - - r = bus_gvariant_is_fixed_size(contents); - if (r < 0) - return r; - - *begin = m->body_size; - *need_offsets = r == 0; - } else { - /* Align contents to 8 byte boundary */ - if (!message_extend_body(m, 8, 0, false, 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, false)) - return -ENOMEM; - - r = bus_gvariant_is_fixed_size(contents); - if (r < 0) - return r; - - *begin = m->body_size; - *need_offsets = r == 0; - } else { - /* Align contents to 8 byte boundary */ - if (!message_extend_body(m, 8, 0, false, 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 = 0; - 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->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 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 = bus_gvariant_determine_word_size(payload, c->n_offsets); - - a = message_extend_body(m, 1, sz * c->n_offsets, true, false); - if (!a) - return -ENOMEM; - - for (i = 0; i < c->n_offsets; i++) - bus_gvariant_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, false); /* 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); - assert(c->signature); - - if (!BUS_MESSAGE_IS_GVARIANT(m)) - return 0; - - l = strlen(c->signature); - - a = message_extend_body(m, 1, 1 + l, true, false); - 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) { - bool fixed_size = true; - 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) - fixed_size = false; - 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 (isempty(c->signature)) { - /* The unary type is encoded as fixed 1 byte padding */ - a = message_extend_body(m, 1, 1, add_offset, false); - if (!a) - return -ENOMEM; - - *a = 0; - } else if (n_variable <= 0) { - int alignment = 1; - - /* Structures with fixed-size members only have to be - * fixed-size themselves. But gvariant requires all fixed-size - * elements to be sized a multiple of their alignment. Hence, - * we must *always* add final padding after the last member so - * the overall size of the structure is properly aligned. */ - if (fixed_size) - alignment = bus_gvariant_get_alignment(strempty(c->signature)); - - assert(alignment > 0); - - a = message_extend_body(m, alignment, 0, add_offset, false); - if (!a) - return -ENOMEM; - } else { - size_t sz; - unsigned j; - - assert(c->offsets[c->n_offsets-1] == m->body_size); - - sz = bus_gvariant_determine_word_size(m->body_size - c->begin, n_variable); - - a = message_extend_body(m, 1, sz * n_variable, add_offset, false); - 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; - - bus_gvariant_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: { - uint64_t x; - - x = va_arg(ap, uint64_t); - r = sd_bus_message_append_basic(m, *t, &x); - break; - } - - case SD_BUS_TYPE_DOUBLE: { - double x; - - x = va_arg(ap, double); - 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, 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; - - memcpy_safe(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 - memzero(p, 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, - int memfd, - uint64_t offset, - uint64_t size) { - - _cleanup_close_ int copy_fd = -1; - struct bus_body_part *part; - ssize_t align, sz; - uint64_t real_size; - void *a; - int r; - - assert_return(m, -EINVAL); - assert_return(memfd >= 0, -EBADF); - assert_return(bus_type_is_trivial(type), -EINVAL); - assert_return(size > 0, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(!m->poisoned, -ESTALE); - - r = memfd_set_sealed(memfd); - if (r < 0) - return r; - - copy_fd = dup(memfd); - if (copy_fd < 0) - return copy_fd; - - r = memfd_get_size(memfd, &real_size); - if (r < 0) - return r; - - if (offset == 0 && size == (uint64_t) -1) - size = real_size; - else if (offset + size > real_size) - return -EMSGSIZE; - - align = bus_type_get_alignment(type); - sz = bus_type_get_size(type); - - assert_se(align > 0); - assert_se(sz > 0); - - if (offset % align != 0) - return -EINVAL; - - 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, false); - if (!a) - return -ENOMEM; - - part = message_append_part(m); - if (!part) - return -ENOMEM; - - part->memfd = copy_fd; - part->memfd_offset = offset; - part->sealed = true; - part->size = size; - copy_fd = -1; - - m->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, - int memfd, - uint64_t offset, - uint64_t size) { - - _cleanup_close_ int copy_fd = -1; - struct bus_body_part *part; - struct bus_container *c; - uint64_t real_size; - void *a; - int r; - - assert_return(m, -EINVAL); - assert_return(memfd >= 0, -EBADF); - assert_return(size > 0, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(!m->poisoned, -ESTALE); - - r = memfd_set_sealed(memfd); - if (r < 0) - return r; - - copy_fd = dup(memfd); - if (copy_fd < 0) - return copy_fd; - - r = memfd_get_size(memfd, &real_size); - if (r < 0) - return r; - - if (offset == 0 && size == (uint64_t) -1) - size = real_size; - else if (offset + size > real_size) - return -EMSGSIZE; - - /* 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, false); - if (!a) - return -ENOMEM; - - *(uint32_t*) a = size - 1; - } - - part = message_append_part(m); - if (!part) - return -ENOMEM; - - part->memfd = copy_fd; - part->memfd_offset = offset; - part->sealed = true; - part->size = size; - copy_fd = -1; - - m->body_size += size; - message_extend_containers(m, size); - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - r = message_add_offset(m, m->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) { - - assert(m); - - /* The actual user data is finished now, we just complete the - variant and struct now (at least on gvariant). Remember - this position, so that during parsing we know where to to - put the outer container end. */ - m->user_body_size = m->body_size; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - const char *signature; - size_t sz, l; - void *d; - - /* Add offset table to end of fields array */ - if (m->n_header_offsets >= 1) { - uint8_t *a; - unsigned i; - - assert(m->fields_size == m->header_offsets[m->n_header_offsets-1]); - - sz = bus_gvariant_determine_word_size(m->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++) - bus_gvariant_write_word_le(a + sz*i, sz, m->header_offsets[i]); - } - - /* Add gvariant NUL byte plus signature to the end of - * the body, followed by the final offset pointing to - * the end of the fields array */ - - signature = strempty(m->root_container.signature); - l = strlen(signature); - - sz = bus_gvariant_determine_word_size(sizeof(struct bus_header) + ALIGN8(m->fields_size) + m->body_size + 1 + l + 2, 1); - d = message_extend_body(m, 1, 1 + l + 2 + sz, false, true); - if (!d) - return -ENOMEM; - - *(uint8_t*) d = 0; - *((uint8_t*) d + 1) = SD_BUS_TYPE_STRUCT_BEGIN; - memcpy((uint8_t*) d + 2, signature, l); - *((uint8_t*) d + 1 + l + 1) = SD_BUS_TYPE_STRUCT_END; - - bus_gvariant_write_word_le((uint8_t*) d + 1 + l + 2, sz, sizeof(struct bus_header) + m->fields_size); - - m->footer = d; - m->footer_accessible = 1 + l + 2 + sz; - } else { - m->header->dbus1.fields_size = m->fields_size; - m->header->dbus1.body_size = m->body_size; - } - - return 0; -} - -int bus_message_seal(sd_bus_message *m, uint64_t cookie, usec_t timeout) { - struct bus_body_part *part; - size_t a; - unsigned i; - int r; - - assert(m); - - if (m->sealed) - return -EPERM; - - if (m->n_containers > 0) - return -EBADMSG; - - if (m->poisoned) - return -ESTALE; - - if (cookie > 0xffffffffULL && - !BUS_MESSAGE_IS_GVARIANT(m)) - return -EOPNOTSUPP; - - /* 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, but only on dbus1 */ - if (!isempty(m->root_container.signature) && !BUS_MESSAGE_IS_GVARIANT(m)) { - 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; - - if (BUS_MESSAGE_IS_GVARIANT(m)) - m->header->dbus2.cookie = cookie; - else - m->header->dbus1.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. */ - a = ALIGN8(m->fields_size) - m->fields_size; - if (a > 0) - memzero((uint8_t*) BUS_MESSAGE_FIELDS(m) + m->fields_size, 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->use_memfd) { - MESSAGE_FOREACH_PART(part, i, m) - if (part->memfd >= 0 && - !part->sealed && - (part->size > MEMFD_MIN_SIZE || m->bus->use_memfd < 0) && - part != m->body_end) { /* The last part may never be sent as memfd */ - 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; - r = memfd_set_size(part->memfd, sz); - if (r < 0) - return r; - - /* Finally, try to seal */ - if (memfd_set_sealed(part->memfd) >= 0) - part->sealed = true; - } - } - - m->root_container.end = m->user_body_size; - 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, shift; - - 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; - } - - shift = part->memfd_offset - ((part->memfd_offset / page_size()) * page_size()); - psz = PAGE_ALIGN(part->size + shift); - - if (part->memfd >= 0) - p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE, part->memfd, part->memfd_offset - shift); - 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->mmap_begin = p; - part->data = (uint8_t*) p + shift; - 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->mmap_begin) - return; - - if (!part->munmap_this) - return; - - assert_se(munmap(part->mmap_begin, part->mapped) == 0); - - part->mmap_begin = NULL; - 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 > m->user_body_size) - 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 { - - 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 = bus_gvariant_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 = bus_gvariant_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 = bus_gvariant_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)) { - /* Unary type is encoded as *fixed* 1 byte padding */ - r = message_peek_body(m, &m->rindex, 1, 1, &q); - if (r < 0) - return r; - - if (*(uint8_t *) q != 0) - return -EBADMSG; - - *item_size = 0; - *offsets = NULL; - *n_offsets = 0; - return 0; - } - - sz = bus_gvariant_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 = bus_gvariant_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 - /* 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; - - /* Unary type has fixed size of 1, but virtual size of 0 */ - if (BUS_MESSAGE_IS_GVARIANT(m) && - type == SD_BUS_TYPE_STRUCT && - isempty(signature)) - w->end = m->rindex + 0; - else - 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; /* length 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); - - /* If types is NULL, read exactly one element */ - if (!types) { - struct bus_container *c; - size_t l; - - if (message_end_of_signature(m)) - return -ENXIO; - - if (message_end_of_array(m, m->rindex)) - return 0; - - c = message_get_container(m); - - r = signature_element_length(c->signature + c->index, &l); - if (r < 0) - return r; - - types = strndupa(c->signature + c->index, l); - } - - switch (*types) { - - case 0: /* Nothing to drop */ - 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_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; - - 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), -EOPNOTSUPP); - - 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), m->fields_size, 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_uint64( - sd_bus_message *m, - size_t *ri, - size_t item_size, - uint64_t *ret) { - - int r; - void *q; - - assert(m); - assert(ri); - - if (BUS_MESSAGE_IS_GVARIANT(m) && item_size != 8) - return -EBADMSG; - - /* identical for gvariant and dbus1 */ - - r = message_peek_fields(m, ri, 8, 8, &q); - if (r < 0) - return r; - - if (ret) - *ret = BUS_MESSAGE_BSWAP64(m, *(uint64_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; - bool unix_fds_set = false; - void *offsets = NULL; - unsigned n_offsets = 0; - size_t sz = 0; - unsigned i = 0; - - assert(m); - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - char *p; - - /* Read the signature from the end of the body variant first */ - sz = bus_gvariant_determine_word_size(BUS_MESSAGE_SIZE(m), 0); - if (m->footer_accessible < 1 + sz) - return -EBADMSG; - - p = (char*) m->footer + m->footer_accessible - (1 + sz); - for (;;) { - if (p < (char*) m->footer) - return -EBADMSG; - - if (*p == 0) { - size_t l; - char *c; - - /* We found the beginning of the signature - * string, yay! We require the body to be a - * structure, so verify it and then strip the - * opening/closing brackets. */ - - l = ((char*) m->footer + m->footer_accessible) - p - (1 + sz); - if (l < 2 || - p[1] != SD_BUS_TYPE_STRUCT_BEGIN || - p[1 + l - 1] != SD_BUS_TYPE_STRUCT_END) - return -EBADMSG; - - c = strndup(p + 1 + 1, l - 2); - if (!c) - return -ENOMEM; - - free(m->root_container.signature); - m->root_container.signature = c; - break; - } - - p--; - } - - /* Calculate the actual user body size, by removing - * the trailing variant signature and struct offset - * table */ - m->user_body_size = m->body_size - ((char*) m->footer + m->footer_accessible - p); - - /* Pull out the offset table for the fields array */ - sz = bus_gvariant_determine_word_size(m->fields_size, 0); - if (sz > 0) { - size_t framing; - void *q; - - ri = m->fields_size - sz; - r = message_peek_fields(m, &ri, 1, sz, &q); - if (r < 0) - return r; - - framing = bus_gvariant_read_word_le(q, sz); - if (framing >= m->fields_size - sz) - return -EBADMSG; - if ((m->fields_size - framing) % sz != 0) - return -EBADMSG; - - ri = framing; - r = message_peek_fields(m, &ri, 1, m->fields_size - framing, &offsets); - if (r < 0) - return r; - - n_offsets = (m->fields_size - framing) / sz; - } - } else - m->user_body_size = m->body_size; - - ri = 0; - while (ri < m->fields_size) { - _cleanup_free_ char *sig = NULL; - const char *signature; - uint64_t field_type; - size_t item_size = (size_t) -1; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - uint64_t *u64; - - if (i >= n_offsets) - break; - - if (i == 0) - ri = 0; - else - ri = ALIGN_TO(bus_gvariant_read_word_le((uint8_t*) offsets + (i-1)*sz, sz), 8); - - r = message_peek_fields(m, &ri, 8, 8, (void**) &u64); - if (r < 0) - return r; - - field_type = BUS_MESSAGE_BSWAP64(m, *u64); - } else { - uint8_t *u8; - - r = message_peek_fields(m, &ri, 8, 1, (void**) &u8); - if (r < 0) - return r; - - field_type = *u8; - } - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - size_t where, end; - char *b; - void *q; - - end = bus_gvariant_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 (field_type) { - - 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->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 (BUS_MESSAGE_IS_GVARIANT(m)) /* only applies to dbus1 */ - return -EBADMSG; - - 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 (BUS_MESSAGE_IS_GVARIANT(m)) { - /* 64bit on dbus2 */ - - if (!streq(signature, "t")) - return -EBADMSG; - - r = message_peek_field_uint64(m, &ri, item_size, &m->reply_cookie); - if (r < 0) - return r; - } else { - /* 32bit on dbus1 */ - uint32_t serial; - - if (!streq(signature, "u")) - return -EBADMSG; - - r = message_peek_field_uint32(m, &ri, item_size, &serial); - if (r < 0) - return r; - - m->reply_cookie = serial; - } - - if (m->reply_cookie == 0) - return -EBADMSG; - - break; - - case BUS_MESSAGE_HEADER_UNIX_FDS: - if (unix_fds_set) - return -EBADMSG; - - if (!streq(signature, "u")) - return -EBADMSG; - - r = message_peek_field_uint32(m, &ri, item_size, &unix_fds); - if (r < 0) - return -EBADMSG; - - unix_fds_set = true; - 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; - - if (m->reply_cookie != 0) - return -EBADMSG; - - break; - - case SD_BUS_MESSAGE_METHOD_CALL: - - if (!m->path || !m->member) - return -EBADMSG; - - if (m->reply_cookie != 0) - 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 = m->user_body_size; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - r = build_struct_offsets( - m, - m->root_container.signature, - m->user_body_size, - &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) - (void) 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) { - const char *s; - int r; - - assert(m); - assert(l); - - r = sd_bus_message_enter_container(m, 'a', "s"); - if (r <= 0) - return r; - - while ((r = sd_bus_message_read_basic(m, 's', &s)) > 0) { - r = strv_extend(l, s); - if (r < 0) - return r; - } - 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; -} - -static int bus_message_get_arg_skip( - sd_bus_message *m, - unsigned i, - char *_type, - const char **_contents) { - - unsigned j; - int r; - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - for (j = 0;; j++) { - const char *contents; - char type; - - r = sd_bus_message_peek_type(m, &type, &contents); - if (r < 0) - return r; - if (r == 0) - return -ENXIO; - - /* Don't match against arguments after the first one we don't understand */ - if (!IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE) && - !(type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g"))) - return -ENXIO; - - if (j >= i) { - if (_contents) - *_contents = contents; - if (_type) - *_type = type; - return 0; - } - - r = sd_bus_message_skip(m, NULL); - if (r < 0) - return r; - } - -} - -int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str) { - char type; - int r; - - assert(m); - assert(str); - - r = bus_message_get_arg_skip(m, i, &type, NULL); - if (r < 0) - return r; - - if (!IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) - return -ENXIO; - - return sd_bus_message_read_basic(m, type, str); -} - -int bus_message_get_arg_strv(sd_bus_message *m, unsigned i, char ***strv) { - const char *contents; - char type; - int r; - - assert(m); - assert(strv); - - r = bus_message_get_arg_skip(m, i, &type, &contents); - if (r < 0) - return r; - - if (type != SD_BUS_TYPE_ARRAY) - return -ENXIO; - if (!STR_IN_SET(contents, "s", "o", "g")) - return -ENXIO; - - return sd_bus_message_read_strv(m, strv); -} - -_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_is_empty(sd_bus_message *m) { - assert_return(m, -EINVAL); - - return isempty(m->root_container.signature); -} - -_public_ int sd_bus_message_has_signature(sd_bus_message *m, const char *signature) { - assert_return(m, -EINVAL); - - return streq(strempty(m->root_container.signature), strempty(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_(sd_bus_message_unrefp) 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, &n, (*m)->path, (*m)->interface, (*m)->member); - if (r < 0) - return r; - - break; - - case SD_BUS_MESSAGE_METHOD_CALL: - r = sd_bus_message_new_method_call(bus, &n, (*m)->destination, (*m)->path, (*m)->interface, (*m)->member); - 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_reply_cookie(n, 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); -} - -_public_ int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority) { - assert_return(m, -EINVAL); - assert_return(priority, -EINVAL); - - *priority = m->priority; - return 0; -} - -_public_ int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority) { - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - m->priority = priority; - return 0; -} diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h deleted file mode 100644 index 4710c106b9..0000000000 --- a/src/libsystemd/sd-bus/bus-message.h +++ /dev/null @@ -1,244 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -#include "sd-bus.h" - -#include "bus-creds.h" -#include "bus-protocol.h" -#include "macro.h" -#include "time-util.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_body_part { - struct bus_body_part *next; - void *data; - void *mmap_begin; - size_t size; - size_t mapped; - size_t allocated; - uint64_t memfd_offset; - 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; - - uint64_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; - uint64_t seqnum; - int64_t priority; - uint64_t verify_destination_id; - - 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; - - /* The first and last bytes of the message */ - struct bus_header *header; - void *footer; - - /* How many bytes are accessible in the above pointers */ - size_t header_accessible; - size_t footer_accessible; - - size_t fields_size; - size_t body_size; - size_t user_body_size; - - 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; - size_t 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]; - char *destination_ptr; - - size_t header_offsets[_BUS_MESSAGE_HEADER_MAX]; - unsigned n_header_offsets; -}; - -static inline bool BUS_MESSAGE_NEED_BSWAP(sd_bus_message *m) { - return 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 uint64_t BUS_MESSAGE_COOKIE(sd_bus_message *m) { - if (m->header->version == 2) - return BUS_MESSAGE_BSWAP64(m, m->header->dbus2.cookie); - - return BUS_MESSAGE_BSWAP32(m, m->header->dbus1.serial); -} - -static inline size_t BUS_MESSAGE_SIZE(sd_bus_message *m) { - return - sizeof(struct bus_header) + - ALIGN8(m->fields_size) + - m->body_size; -} - -static inline size_t BUS_MESSAGE_BODY_BEGIN(sd_bus_message *m) { - return - sizeof(struct bus_header) + - ALIGN8(m->fields_size); -} - -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 header_accessible, - void *footer, - size_t footer_accessible, - size_t message_size, - int *fds, - unsigned n_fds, - 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 char *label, - sd_bus_message **ret); - -int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str); -int bus_message_get_arg_strv(sd_bus_message *m, unsigned i, char ***strv); - -int bus_message_append_ap(sd_bus_message *m, const char *types, va_list ap); - -int bus_message_parse_fields(sd_bus_message *m); - -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); - -void bus_message_set_sender_driver(sd_bus *bus, sd_bus_message *m); -void bus_message_set_sender_local(sd_bus *bus, sd_bus_message *m); diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c deleted file mode 100644 index 9bd07ffcab..0000000000 --- a/src/libsystemd/sd-bus/bus-objects.c +++ /dev/null @@ -1,2806 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-introspect.h" -#include "bus-message.h" -#include "bus-objects.h" -#include "bus-signature.h" -#include "bus-slot.h" -#include "bus-type.h" -#include "bus-util.h" -#include "set.h" -#include "string-util.h" -#include "strv.h" - -static int node_vtable_get_userdata( - sd_bus *bus, - const char *path, - struct node_vtable *c, - void **userdata, - sd_bus_error *error) { - - sd_bus_slot *s; - void *u; - int r; - - assert(bus); - assert(path); - assert(c); - - s = container_of(c, sd_bus_slot, node_vtable); - u = s->userdata; - if (c->find) { - bus->current_slot = sd_bus_slot_ref(s); - bus->current_userdata = u; - r = c->find(bus, path, c->interface, u, &u, error); - bus->current_userdata = NULL; - bus->current_slot = sd_bus_slot_unref(s); - - 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_method_convert_userdata(const sd_bus_vtable *p, void *u) { - assert(p); - - return (uint8_t*) u + p->x.method.offset; -} - -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; - sd_bus_slot *slot; - - if (bus->nodes_modified) - return 0; - - slot = container_of(c, sd_bus_slot, node_enumerator); - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_userdata = slot->userdata; - r = c->callback(bus, prefix, slot->userdata, &children, error); - bus->current_userdata = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - - 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; -} - -enum { - /* if set, add_subtree() works recursively */ - CHILDREN_RECURSIVE = (1U << 1), - /* if set, add_subtree() scans object-manager hierarchies recursively */ - CHILDREN_SUBHIERARCHIES = (1U << 0), -}; - -static int add_subtree_to_set( - sd_bus *bus, - const char *prefix, - struct node *n, - unsigned int flags, - 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; - - if ((flags & CHILDREN_RECURSIVE) && - ((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) { - r = add_subtree_to_set(bus, prefix, i, flags, 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, - unsigned int flags, - Set **_s, - sd_bus_error *error) { - - Set *s = NULL; - int r; - - assert(bus); - assert(prefix); - assert(n); - assert(_s); - - s = set_new(&string_hash_ops); - if (!s) - return -ENOMEM; - - r = add_subtree_to_set(bus, prefix, n, flags, 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_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; - sd_bus_slot *slot; - - 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; - - slot = container_of(c, sd_bus_slot, node_callback); - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = c->callback; - bus->current_userdata = slot->userdata; - r = c->callback(m, slot->userdata, &error_buffer); - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - - 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) { - uint64_t cap; - 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; - - /* 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_query_sender_privilege(m, cap); - if (r < 0) - return r; - if (r > 0) - return 0; - - 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_(sd_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; - - u = vtable_method_convert_userdata(c->vtable, u); - - *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) { - sd_bus_slot *slot; - - slot = container_of(c->parent, sd_bus_slot, node_vtable); - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = c->vtable->x.method.handler; - bus->current_userdata = u; - r = c->vtable->x.method.handler(m, u, &error); - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - - 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, - sd_bus_slot *slot, - 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(slot); - assert(v); - assert(path); - assert(interface); - assert(property); - assert(reply); - - if (v->x.property.get) { - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_userdata = userdata; - r = v->x.property.get(bus, path, interface, property, reply, userdata, error); - bus->current_userdata = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - - 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, - sd_bus_slot *slot, - 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(slot); - assert(v); - assert(path); - assert(interface); - assert(property); - assert(value); - - if (v->x.property.set) { - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_userdata = userdata; - r = v->x.property.set(bus, path, interface, property, value, userdata, error); - bus->current_userdata = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - - 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - sd_bus_slot *slot; - void *u = NULL; - 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; - - slot = container_of(c->parent, sd_bus_slot, node_vtable); - - *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, slot, 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 { - const char *signature = NULL; - char type = 0; - - 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_peek_type(m, &type, &signature); - if (r < 0) - return r; - - if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature))) - return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Incorrect parameters for property '%s', expected '%s', got '%s'.", c->member, strempty(c->vtable->x.property.signature), strempty(signature)); - - 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, slot, 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) { - - sd_bus_slot *slot; - 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; - - slot = container_of(c, sd_bus_slot, node_vtable); - - r = invoke_property_get(bus, slot, 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; - - if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT) - 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_(sd_bus_message_unrefp) 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_(sd_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 int bus_node_exists( - sd_bus *bus, - struct node *n, - const char *path, - bool require_fallback) { - - struct node_vtable *c; - struct node_callback *k; - int r; - - assert(bus); - assert(n); - assert(path); - - /* Tests if there's anything attached directly to this node - * for the specified path */ - - if (!require_fallback && (n->enumerators || n->object_managers)) - return true; - - LIST_FOREACH(callbacks, k, n->callbacks) { - if (require_fallback && !k->is_fallback) - continue; - - return 1; - } - - LIST_FOREACH(vtables, c, n->vtables) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - if (require_fallback && !c->is_fallback) - continue; - - r = node_vtable_get_userdata(bus, path, c, NULL, &error); - if (r != 0) - return r; - if (bus->nodes_modified) - return 0; - } - - return 0; -} - -static int process_introspect( - sd_bus *bus, - sd_bus_message *m, - struct node *n, - bool require_fallback, - bool *found_object) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) 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, 0, &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, !require_fallback && n->object_managers); - 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(" \n", intro.f); - - fprintf(intro.f, " \n", c->interface); - } - - r = introspect_write_interface(&intro, c->vtable); - if (r < 0) - goto finish; - - previous_interface = c->interface; - } - - if (previous_interface) - fputs(" \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) - goto finish; - if (bus->nodes_modified) { - 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_set_free_free_ Set *s = NULL; - Iterator i; - char *path; - int r; - - assert(bus); - assert(m); - assert(n); - assert(found_object); - - /* Spec says, GetManagedObjects() is only implemented on the root of a - * sub-tree. Therefore, we require a registered object-manager on - * exactly the queried path, otherwise, we refuse to respond. */ - - if (require_fallback || !n->object_managers) - return 0; - - r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &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; - - 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 (bus->nodes_modified) - return 0; - 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 (bus->hello_flags & KDBUS_HELLO_MONITOR) - return 0; - - 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_ops); - 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, e - path)); - - 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; -} - -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_managers) - 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_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) { - struct node *n; - - assert(bus); - assert(path); - - n = hashmap_get(bus->nodes, path); - if (!n) { - char *prefix; - - prefix = alloca(strlen(path) + 1); - OBJECT_PATH_FOREACH_PREFIX(prefix, path) { - n = hashmap_get(bus->nodes, prefix); - if (n) - break; - } - } - - while (n && !n->object_managers) - n = n->parent; - - if (out) - *out = n; - return !!n; -} - -static int bus_add_object( - sd_bus *bus, - sd_bus_slot **slot, - bool fallback, - const char *path, - sd_bus_message_handler_t callback, - void *userdata) { - - sd_bus_slot *s; - 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; - - s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata); - if (!s) { - r = -ENOMEM; - goto fail; - } - - s->node_callback.callback = callback; - s->node_callback.is_fallback = fallback; - - s->node_callback.node = n; - LIST_PREPEND(callbacks, n->callbacks, &s->node_callback); - bus->nodes_modified = true; - - if (slot) - *slot = s; - - return 0; - -fail: - sd_bus_slot_unref(s); - bus_node_gc(bus, n); - - return r; -} - -_public_ int sd_bus_add_object( - sd_bus *bus, - sd_bus_slot **slot, - const char *path, - sd_bus_message_handler_t callback, - void *userdata) { - - return bus_add_object(bus, slot, false, path, callback, userdata); -} - -_public_ int sd_bus_add_fallback( - sd_bus *bus, - sd_bus_slot **slot, - const char *prefix, - sd_bus_message_handler_t callback, - void *userdata) { - - return bus_add_object(bus, slot, true, prefix, callback, userdata); -} - -static void vtable_member_hash_func(const void *a, struct siphash *state) { - const struct vtable_member *m = a; - - assert(m); - - string_hash_func(m->path, state); - string_hash_func(m->interface, state); - string_hash_func(m->member, state); -} - -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 const struct hash_ops vtable_member_hash_ops = { - .hash = vtable_member_hash_func, - .compare = vtable_member_compare_func -}; - -static int add_object_vtable_internal( - sd_bus *bus, - sd_bus_slot **slot, - const char *path, - const char *interface, - const sd_bus_vtable *vtable, - bool fallback, - sd_bus_object_find_t find, - void *userdata) { - - sd_bus_slot *s = NULL; - struct node_vtable *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_ops); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops); - 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; - } - } - - s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata); - if (!s) { - r = -ENOMEM; - goto fail; - } - - s->node_vtable.is_fallback = fallback; - s->node_vtable.vtable = vtable; - s->node_vtable.find = find; - - s->node_vtable.interface = strdup(interface); - if (!s->node_vtable.interface) { - r = -ENOMEM; - goto fail; - } - - for (v = s->node_vtable.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 = &s->node_vtable; - m->path = n->path; - m->interface = s->node_vtable.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; - } - - if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) { - 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_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) || - (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 = &s->node_vtable; - m->path = n->path; - m->interface = s->node_vtable.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; - } - } - - s->node_vtable.node = n; - LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable); - bus->nodes_modified = true; - - if (slot) - *slot = s; - - return 0; - -fail: - sd_bus_slot_unref(s); - bus_node_gc(bus, n); - - return r; -} - -_public_ int sd_bus_add_object_vtable( - sd_bus *bus, - sd_bus_slot **slot, - const char *path, - const char *interface, - const sd_bus_vtable *vtable, - void *userdata) { - - return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata); -} - -_public_ int sd_bus_add_fallback_vtable( - sd_bus *bus, - sd_bus_slot **slot, - const char *prefix, - const char *interface, - const sd_bus_vtable *vtable, - sd_bus_object_find_t find, - void *userdata) { - - return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata); -} - -_public_ int sd_bus_add_node_enumerator( - sd_bus *bus, - sd_bus_slot **slot, - const char *path, - sd_bus_node_enumerator_t callback, - void *userdata) { - - sd_bus_slot *s; - 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; - - s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata); - if (!s) { - r = -ENOMEM; - goto fail; - } - - s->node_enumerator.callback = callback; - - s->node_enumerator.node = n; - LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator); - bus->nodes_modified = true; - - if (slot) - *slot = s; - - return 0; - -fail: - sd_bus_slot_unref(s); - bus_node_gc(bus, n); - - return r; -} - -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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) 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, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged"); - 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_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - /* 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_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (!name) - return 0; - - names = strv_from_stdarg_alloca(name); - - return sd_bus_emit_properties_changed_strv(bus, path, interface, names); -} - -static int object_added_append_all_prefix( - sd_bus *bus, - sd_bus_message *m, - Set *s, - const char *prefix, - const char *path, - bool require_fallback) { - - const char *previous_interface = NULL; - struct node_vtable *c; - struct node *n; - int r; - - assert(bus); - assert(m); - assert(s); - assert(prefix); - assert(path); - - n = hashmap_get(bus->nodes, prefix); - if (!n) - return 0; - - LIST_FOREACH(vtables, c, n->vtables) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - void *u = NULL; - - if (require_fallback && !c->is_fallback) - 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 (!streq_ptr(c->interface, previous_interface)) { - /* If a child-node already handled this interface, we - * skip it on any of its parents. The child vtables - * always fully override any conflicting vtables of - * any parent node. */ - if (set_get(s, c->interface)) - continue; - - r = set_put(s, c->interface); - if (r < 0) - return r; - - if (previous_interface) { - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - } - - r = sd_bus_message_open_container(m, 'e', "sa{sv}"); - if (r < 0) - return r; - r = sd_bus_message_append(m, "s", c->interface); - if (r < 0) - return r; - r = sd_bus_message_open_container(m, 'a', "{sv}"); - if (r < 0) - return r; - - previous_interface = c->interface; - } - - r = vtable_append_all_properties(bus, m, path, c, u, &error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - } - - if (previous_interface) { - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - } - - return 0; -} - -static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { - _cleanup_set_free_ Set *s = NULL; - char *prefix; - int r; - - assert(bus); - assert(m); - assert(path); - - /* - * This appends all interfaces registered on path @path. We first add - * the builtin interfaces, which are always available and handled by - * sd-bus. Then, we add all interfaces registered on the exact node, - * followed by all fallback interfaces registered on any parent prefix. - * - * If an interface is registered multiple times on the same node with - * different vtables, we merge all the properties across all vtables. - * However, if a child node has the same interface registered as one of - * its parent nodes has as fallback, we make the child overwrite the - * parent instead of extending it. Therefore, we keep a "Set" of all - * handled interfaces during parent traversal, so we skip interfaces on - * a parent that were overwritten by a child. - */ - - s = set_new(&string_hash_ops); - if (!s) - return -ENOMEM; - - r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0); - if (r < 0) - return r; - r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0); - if (r < 0) - return r; - r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0); - if (r < 0) - return r; - r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0); - if (r < 0) - return r; - - r = object_added_append_all_prefix(bus, m, s, path, path, false); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - - prefix = alloca(strlen(path) + 1); - OBJECT_PATH_FOREACH_PREFIX(prefix, path) { - r = object_added_append_all_prefix(bus, m, s, prefix, path, true); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - } - - return 0; -} - -_public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) { - BUS_DONT_DESTROY(bus); - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - struct node *object_manager; - int r; - - /* - * This emits an InterfacesAdded signal on the given path, by iterating - * all registered vtables and fallback vtables on the path. All - * properties are queried and included in the signal. - * This call is equivalent to sd_bus_emit_interfaces_added() with an - * explicit list of registered interfaces. However, unlike - * interfaces_added(), this call can figure out the list of supported - * interfaces itself. Furthermore, it properly adds the builtin - * org.freedesktop.DBus.* interfaces. - */ - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - r = bus_find_parent_object_manager(bus, &object_manager, path); - if (r < 0) - return r; - if (r == 0) - return -ESRCH; - - do { - bus->nodes_modified = false; - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"); - 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; - - r = object_added_append_all(bus, m, path); - 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); -} - -static int object_removed_append_all_prefix( - sd_bus *bus, - sd_bus_message *m, - Set *s, - const char *prefix, - const char *path, - bool require_fallback) { - - const char *previous_interface = NULL; - struct node_vtable *c; - struct node *n; - int r; - - assert(bus); - assert(m); - assert(s); - assert(prefix); - assert(path); - - n = hashmap_get(bus->nodes, prefix); - if (!n) - return 0; - - LIST_FOREACH(vtables, c, n->vtables) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - void *u = NULL; - - if (require_fallback && !c->is_fallback) - continue; - if (streq_ptr(c->interface, previous_interface)) - continue; - - /* If a child-node already handled this interface, we - * skip it on any of its parents. The child vtables - * always fully override any conflicting vtables of - * any parent node. */ - if (set_get(s, c->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; - - r = set_put(s, c->interface); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "s", c->interface); - if (r < 0) - return r; - - previous_interface = c->interface; - } - - return 0; -} - -static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { - _cleanup_set_free_ Set *s = NULL; - char *prefix; - int r; - - assert(bus); - assert(m); - assert(path); - - /* see sd_bus_emit_object_added() for details */ - - s = set_new(&string_hash_ops); - if (!s) - return -ENOMEM; - - r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer"); - if (r < 0) - return r; - r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable"); - if (r < 0) - return r; - r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties"); - if (r < 0) - return r; - r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager"); - if (r < 0) - return r; - - r = object_removed_append_all_prefix(bus, m, s, path, path, false); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - - prefix = alloca(strlen(path) + 1); - OBJECT_PATH_FOREACH_PREFIX(prefix, path) { - r = object_removed_append_all_prefix(bus, m, s, prefix, path, true); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - } - - return 0; -} - -_public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) { - BUS_DONT_DESTROY(bus); - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - struct node *object_manager; - int r; - - /* - * This is like sd_bus_emit_object_added(), but emits an - * InterfacesRemoved signal on the given path. This only includes any - * registered interfaces but skips the properties. Note that this will - * call into the find() callbacks of any registered vtable. Therefore, - * you must call this function before destroying/unlinking your object. - * Otherwise, the list of interfaces will be incomplete. However, note - * that this will *NOT* call into any property callback. Therefore, the - * object might be in an "destructed" state, as long as we can find it. - */ - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - r = bus_find_parent_object_manager(bus, &object_manager, path); - if (r < 0) - return r; - if (r == 0) - return -ESRCH; - - do { - bus->nodes_modified = false; - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"); - 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', "s"); - if (r < 0) - return r; - - r = object_removed_append_all(bus, m, path); - 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); -} - -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_(sd_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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - struct node *object_manager; - char **i; - int r; - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (strv_isempty(interfaces)) - return 0; - - r = bus_find_parent_object_manager(bus, &object_manager, path); - if (r < 0) - return r; - if (r == 0) - return -ESRCH; - - do { - bus->nodes_modified = false; - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"); - 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_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - 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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - struct node *object_manager; - int r; - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (strv_isempty(interfaces)) - return 0; - - r = bus_find_parent_object_manager(bus, &object_manager, path); - if (r < 0) - return r; - if (r == 0) - return -ESRCH; - - r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"); - 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_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - 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, sd_bus_slot **slot, const char *path) { - sd_bus_slot *s; - struct node *n; - int r; - - 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; - - s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL); - if (!s) { - r = -ENOMEM; - goto fail; - } - - s->node_object_manager.node = n; - LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager); - bus->nodes_modified = true; - - if (slot) - *slot = s; - - return 0; - -fail: - sd_bus_slot_unref(s); - bus_node_gc(bus, n); - - return r; -} diff --git a/src/libsystemd/sd-bus/bus-objects.h b/src/libsystemd/sd-bus/bus-objects.h deleted file mode 100644 index e0b8c534ed..0000000000 --- a/src/libsystemd/sd-bus/bus-objects.h +++ /dev/null @@ -1,25 +0,0 @@ -#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 . -***/ - -#include "bus-internal.h" - -int bus_process_object(sd_bus *bus, sd_bus_message *m); -void bus_node_gc(sd_bus *b, struct node *n); diff --git a/src/libsystemd/sd-bus/bus-protocol.h b/src/libsystemd/sd-bus/bus-protocol.h deleted file mode 100644 index 9d180cb284..0000000000 --- a/src/libsystemd/sd-bus/bus-protocol.h +++ /dev/null @@ -1,180 +0,0 @@ -#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 . -***/ - -#include - -#include "macro.h" - -/* Packet header */ - -struct _packed_ bus_header { - /* The first four fields are identical for dbus1, and dbus2 */ - uint8_t endian; - uint8_t type; - uint8_t flags; - uint8_t version; - - union _packed_ { - /* dbus1: Used for SOCK_STREAM connections */ - struct _packed_ { - 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; - } dbus1; - - /* dbus2: Used for kdbus connections */ - struct _packed_ { - uint32_t _reserved; - uint64_t cookie; - } dbus2; - - /* Note that both header versions have the same size! */ - }; -}; - -/* 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, - BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION = 4, -}; - -/* 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 \ - "\n" - -#define BUS_INTROSPECT_INTERFACE_PEER \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define BUS_INTROSPECT_INTERFACE_PROPERTIES \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" diff --git a/src/libsystemd/sd-bus/bus-signature.c b/src/libsystemd/sd-bus/bus-signature.c deleted file mode 100644 index 7bc243494a..0000000000 --- a/src/libsystemd/sd-bus/bus-signature.c +++ /dev/null @@ -1,158 +0,0 @@ -/*** - 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 . -***/ - -#include - -#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 deleted file mode 100644 index 1e0cd7f587..0000000000 --- a/src/libsystemd/sd-bus/bus-signature.h +++ /dev/null @@ -1,28 +0,0 @@ -#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 . -***/ - -#include - -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-slot.c b/src/libsystemd/sd-bus/bus-slot.c deleted file mode 100644 index 8e9074c7df..0000000000 --- a/src/libsystemd/sd-bus/bus-slot.c +++ /dev/null @@ -1,286 +0,0 @@ -/*** - 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 . -***/ - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-control.h" -#include "bus-objects.h" -#include "bus-slot.h" -#include "string-util.h" - -sd_bus_slot *bus_slot_allocate( - sd_bus *bus, - bool floating, - BusSlotType type, - size_t extra, - void *userdata) { - - sd_bus_slot *slot; - - assert(bus); - - slot = malloc0(offsetof(sd_bus_slot, reply_callback) + extra); - if (!slot) - return NULL; - - slot->n_ref = 1; - slot->type = type; - slot->bus = bus; - slot->floating = floating; - slot->userdata = userdata; - - if (!floating) - sd_bus_ref(bus); - - LIST_PREPEND(slots, bus->slots, slot); - - return slot; -} - -_public_ sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot) { - - if (!slot) - return NULL; - - assert(slot->n_ref > 0); - - slot->n_ref++; - return slot; -} - -void bus_slot_disconnect(sd_bus_slot *slot) { - sd_bus *bus; - - assert(slot); - - if (!slot->bus) - return; - - switch (slot->type) { - - case BUS_REPLY_CALLBACK: - - if (slot->reply_callback.cookie != 0) - ordered_hashmap_remove(slot->bus->reply_callbacks, &slot->reply_callback.cookie); - - if (slot->reply_callback.timeout != 0) - prioq_remove(slot->bus->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx); - - break; - - case BUS_FILTER_CALLBACK: - slot->bus->filter_callbacks_modified = true; - LIST_REMOVE(callbacks, slot->bus->filter_callbacks, &slot->filter_callback); - break; - - case BUS_MATCH_CALLBACK: - - if (slot->match_added) - bus_remove_match_internal(slot->bus, slot->match_callback.match_string, slot->match_callback.cookie); - - slot->bus->match_callbacks_modified = true; - bus_match_remove(&slot->bus->match_callbacks, &slot->match_callback); - - free(slot->match_callback.match_string); - - break; - - case BUS_NODE_CALLBACK: - - if (slot->node_callback.node) { - LIST_REMOVE(callbacks, slot->node_callback.node->callbacks, &slot->node_callback); - slot->bus->nodes_modified = true; - - bus_node_gc(slot->bus, slot->node_callback.node); - } - - break; - - case BUS_NODE_ENUMERATOR: - - if (slot->node_enumerator.node) { - LIST_REMOVE(enumerators, slot->node_enumerator.node->enumerators, &slot->node_enumerator); - slot->bus->nodes_modified = true; - - bus_node_gc(slot->bus, slot->node_enumerator.node); - } - - break; - - case BUS_NODE_OBJECT_MANAGER: - - if (slot->node_object_manager.node) { - LIST_REMOVE(object_managers, slot->node_object_manager.node->object_managers, &slot->node_object_manager); - slot->bus->nodes_modified = true; - - bus_node_gc(slot->bus, slot->node_object_manager.node); - } - - break; - - case BUS_NODE_VTABLE: - - if (slot->node_vtable.node && slot->node_vtable.interface && slot->node_vtable.vtable) { - const sd_bus_vtable *v; - - for (v = slot->node_vtable.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 = slot->node_vtable.node->path; - key.interface = slot->node_vtable.interface; - key.member = v->x.method.member; - - x = hashmap_remove(slot->bus->vtable_methods, &key); - break; - } - - case _SD_BUS_VTABLE_PROPERTY: - case _SD_BUS_VTABLE_WRITABLE_PROPERTY: { - struct vtable_member key; - - key.path = slot->node_vtable.node->path; - key.interface = slot->node_vtable.interface; - key.member = v->x.method.member; - - - x = hashmap_remove(slot->bus->vtable_properties, &key); - break; - }} - - free(x); - } - } - - free(slot->node_vtable.interface); - - if (slot->node_vtable.node) { - LIST_REMOVE(vtables, slot->node_vtable.node->vtables, &slot->node_vtable); - slot->bus->nodes_modified = true; - - bus_node_gc(slot->bus, slot->node_vtable.node); - } - - break; - - default: - assert_not_reached("Wut? Unknown slot type?"); - } - - bus = slot->bus; - - slot->type = _BUS_SLOT_INVALID; - slot->bus = NULL; - LIST_REMOVE(slots, bus->slots, slot); - - if (!slot->floating) - sd_bus_unref(bus); -} - -_public_ sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) { - - if (!slot) - return NULL; - - assert(slot->n_ref > 0); - - if (slot->n_ref > 1) { - slot->n_ref--; - return NULL; - } - - bus_slot_disconnect(slot); - free(slot->description); - free(slot); - - return NULL; -} - -_public_ sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot) { - assert_return(slot, NULL); - - return slot->bus; -} - -_public_ void *sd_bus_slot_get_userdata(sd_bus_slot *slot) { - assert_return(slot, NULL); - - return slot->userdata; -} - -_public_ void *sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata) { - void *ret; - - assert_return(slot, NULL); - - ret = slot->userdata; - slot->userdata = userdata; - - return ret; -} - -_public_ sd_bus_message *sd_bus_slot_get_current_message(sd_bus_slot *slot) { - assert_return(slot, NULL); - assert_return(slot->type >= 0, NULL); - - if (slot->bus->current_slot != slot) - return NULL; - - return slot->bus->current_message; -} - -_public_ sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *slot) { - assert_return(slot, NULL); - assert_return(slot->type >= 0, NULL); - - if (slot->bus->current_slot != slot) - return NULL; - - return slot->bus->current_handler; -} - -_public_ void* sd_bus_slot_get_current_userdata(sd_bus_slot *slot) { - assert_return(slot, NULL); - assert_return(slot->type >= 0, NULL); - - if (slot->bus->current_slot != slot) - return NULL; - - return slot->bus->current_userdata; -} - -_public_ int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description) { - assert_return(slot, -EINVAL); - - return free_and_strdup(&slot->description, description); -} - -_public_ int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description) { - assert_return(slot, -EINVAL); - assert_return(description, -EINVAL); - assert_return(slot->description, -ENXIO); - - *description = slot->description; - return 0; -} diff --git a/src/libsystemd/sd-bus/bus-slot.h b/src/libsystemd/sd-bus/bus-slot.h deleted file mode 100644 index 3b8b94dc6b..0000000000 --- a/src/libsystemd/sd-bus/bus-slot.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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 . -***/ - -#include "sd-bus.h" - -#include "bus-internal.h" - -sd_bus_slot *bus_slot_allocate(sd_bus *bus, bool floating, BusSlotType type, size_t extra, void *userdata); - -void bus_slot_disconnect(sd_bus_slot *slot); diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c deleted file mode 100644 index f1e2a06050..0000000000 --- a/src/libsystemd/sd-bus/bus-socket.c +++ /dev/null @@ -1,1064 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include - -#include "sd-bus.h" -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-socket.h" -#include "fd-util.h" -#include "formats-util.h" -#include "hexdecoct.h" -#include "macro.h" -#include "missing.h" -#include "selinux-util.h" -#include "signal-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "user-util.h" -#include "utf8.h" -#include "util.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_safe(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 == strlen("\r\nAGREE_UNIX_FD")) && - memcmp(e + 2, "AGREE_UNIX_FD", strlen("AGREE_UNIX_FD")) == 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; - size_t len; - int r; - - if (!b->anonymous_auth) - return 0; - - if (l <= 0) - return 1; - - assert(p[0] == ' '); - p++; l--; - - if (l % 2 != 0) - return 0; - - r = unhexmem(p, l, (void **) &token, &len); - if (r < 0) - return 0; - - if (memchr(token, 0, len)) - 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; - size_t len; - 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; - - r = unhexmem(p, l, (void**) &token, &len); - if (r < 0) - return 0; - - if (memchr(token, 0, len)) - 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_safe(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); - - xsprintf(t, "OK " SD_ID128_FORMAT_STR "\r\n", SD_ID128_FORMAT_VAL(b->server_id)); - - 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)]; - } control; - 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; - - 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) { - struct cmsghdr *cmsg; - - CMSG_FOREACH(cmsg, &mh) - 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 - log_debug("Got unexpected auxiliary data with level=%d and type=%d", - cmsg->cmsg_level, cmsg->cmsg_type); - } - - r = bus_socket_auth_verify(b); - if (r != 0) - return r; - - return 1; -} - -void bus_socket_setup(sd_bus *b) { - assert(b); - - /* 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) { - int r; - - assert(b); - - /* Get the peer for socketpair() sockets */ - b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0; - - /* Get the SELinux context of the peer */ - if (mac_selinux_have()) { - r = getpeersec(b->input_fd, &b->label); - if (r < 0 && r != -EOPNOTSUPP) - log_debug_errno(r, "Failed to determine peer security context: %m"); - } -} - -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[DECIMAL_STR_MAX(uid_t) + 1]; - - auth_prefix = "\0AUTH EXTERNAL "; - - xsprintf(text, UID_FMT, geteuid()); - - 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) { - safe_close_pair(s); - return -errno; - } - if (pid == 0) { - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - 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) - safe_close(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); - } - - safe_close(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_safe(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 = { - .msg_iov = iov, - .msg_iovlen = m->n_iovec, - }; - - if (m->n_fds > 0) { - struct cmsghdr *control; - - mh.msg_control = control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds)); - mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds); - control->cmsg_level = SOL_SOCKET; - control->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(control), m->fds, sizeof(int) * m->n_fds); - } - - 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, - 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)]; - } control; - 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; - - 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) { - struct cmsghdr *cmsg; - - CMSG_FOREACH(cmsg, &mh) - 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_safe(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int)); - bus->fds = f; - bus->n_fds += n; - } else - log_debug("Got unexpected auxiliary data with level=%d and type=%d", - cmsg->cmsg_level, cmsg->cmsg_type); - } - - 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 deleted file mode 100644 index 684feead74..0000000000 --- a/src/libsystemd/sd-bus/bus-socket.h +++ /dev/null @@ -1,37 +0,0 @@ -#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 . -***/ - -#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-track.c b/src/libsystemd/sd-bus/bus-track.c deleted file mode 100644 index 1f436fe560..0000000000 --- a/src/libsystemd/sd-bus/bus-track.c +++ /dev/null @@ -1,337 +0,0 @@ -/*** - 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 . -***/ - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-track.h" -#include "bus-util.h" - -struct sd_bus_track { - unsigned n_ref; - sd_bus *bus; - sd_bus_track_handler_t handler; - void *userdata; - Hashmap *names; - LIST_FIELDS(sd_bus_track, queue); - Iterator iterator; - bool in_queue; - bool modified; -}; - -#define MATCH_PREFIX \ - "type='signal'," \ - "sender='org.freedesktop.DBus'," \ - "path='/org/freedesktop/DBus'," \ - "interface='org.freedesktop.DBus'," \ - "member='NameOwnerChanged'," \ - "arg0='" - -#define MATCH_SUFFIX \ - "'" - -#define MATCH_FOR_NAME(name) \ - ({ \ - char *_x; \ - size_t _l = strlen(name); \ - _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \ - strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \ - _x; \ - }) - -static void bus_track_add_to_queue(sd_bus_track *track) { - assert(track); - - if (track->in_queue) - return; - - if (!track->handler) - return; - - LIST_PREPEND(queue, track->bus->track_queue, track); - track->in_queue = true; -} - -static void bus_track_remove_from_queue(sd_bus_track *track) { - assert(track); - - if (!track->in_queue) - return; - - LIST_REMOVE(queue, track->bus->track_queue, track); - track->in_queue = false; -} - -_public_ int sd_bus_track_new( - sd_bus *bus, - sd_bus_track **track, - sd_bus_track_handler_t handler, - void *userdata) { - - sd_bus_track *t; - - assert_return(bus, -EINVAL); - assert_return(track, -EINVAL); - - if (!bus->bus_client) - return -EINVAL; - - t = new0(sd_bus_track, 1); - if (!t) - return -ENOMEM; - - t->n_ref = 1; - t->handler = handler; - t->userdata = userdata; - t->bus = sd_bus_ref(bus); - - bus_track_add_to_queue(t); - - *track = t; - return 0; -} - -_public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) { - - if (!track) - return NULL; - - assert(track->n_ref > 0); - - track->n_ref++; - - return track; -} - -_public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) { - const char *n; - - if (!track) - return NULL; - - assert(track->n_ref > 0); - - if (track->n_ref > 1) { - track->n_ref--; - return NULL; - } - - while ((n = hashmap_first_key(track->names))) - sd_bus_track_remove_name(track, n); - - bus_track_remove_from_queue(track); - hashmap_free(track->names); - sd_bus_unref(track->bus); - free(track); - - return NULL; -} - -static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - sd_bus_track *track = userdata; - const char *name, *old, *new; - int r; - - assert(message); - assert(track); - - r = sd_bus_message_read(message, "sss", &name, &old, &new); - if (r < 0) - return 0; - - sd_bus_track_remove_name(track, name); - return 0; -} - -_public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) { - _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL; - _cleanup_free_ char *n = NULL; - const char *match; - int r; - - assert_return(track, -EINVAL); - assert_return(service_name_is_valid(name), -EINVAL); - - r = hashmap_ensure_allocated(&track->names, &string_hash_ops); - if (r < 0) - return r; - - n = strdup(name); - if (!n) - return -ENOMEM; - - /* First, subscribe to this name */ - match = MATCH_FOR_NAME(n); - r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track); - if (r < 0) - return r; - - r = hashmap_put(track->names, n, slot); - if (r == -EEXIST) - return 0; - if (r < 0) - return r; - - /* Second, check if it is currently existing, or maybe - * doesn't, or maybe disappeared already. */ - r = sd_bus_get_name_creds(track->bus, n, 0, NULL); - if (r < 0) { - hashmap_remove(track->names, n); - return r; - } - - n = NULL; - slot = NULL; - - bus_track_remove_from_queue(track); - track->modified = true; - - return 1; -} - -_public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) { - _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL; - _cleanup_free_ char *n = NULL; - - assert_return(name, -EINVAL); - - if (!track) - return 0; - - slot = hashmap_remove2(track->names, (char*) name, (void**) &n); - if (!slot) - return 0; - - if (hashmap_isempty(track->names)) - bus_track_add_to_queue(track); - - track->modified = true; - - return 1; -} - -_public_ unsigned sd_bus_track_count(sd_bus_track *track) { - if (!track) - return 0; - - return hashmap_size(track->names); -} - -_public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) { - assert_return(track, NULL); - assert_return(name, NULL); - - return hashmap_get(track->names, (void*) name) ? name : NULL; -} - -_public_ const char* sd_bus_track_first(sd_bus_track *track) { - const char *n = NULL; - - if (!track) - return NULL; - - track->modified = false; - track->iterator = ITERATOR_FIRST; - - hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n); - return n; -} - -_public_ const char* sd_bus_track_next(sd_bus_track *track) { - const char *n = NULL; - - if (!track) - return NULL; - - if (track->modified) - return NULL; - - hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n); - return n; -} - -_public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) { - const char *sender; - - assert_return(track, -EINVAL); - assert_return(m, -EINVAL); - - sender = sd_bus_message_get_sender(m); - if (!sender) - return -EINVAL; - - return sd_bus_track_add_name(track, sender); -} - -_public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) { - const char *sender; - - assert_return(track, -EINVAL); - assert_return(m, -EINVAL); - - sender = sd_bus_message_get_sender(m); - if (!sender) - return -EINVAL; - - return sd_bus_track_remove_name(track, sender); -} - -_public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) { - assert_return(track, NULL); - - return track->bus; -} - -void bus_track_dispatch(sd_bus_track *track) { - int r; - - assert(track); - assert(track->in_queue); - assert(track->handler); - - bus_track_remove_from_queue(track); - - sd_bus_track_ref(track); - - r = track->handler(track, track->userdata); - if (r < 0) - log_debug_errno(r, "Failed to process track handler: %m"); - else if (r == 0) - bus_track_add_to_queue(track); - - sd_bus_track_unref(track); -} - -_public_ void *sd_bus_track_get_userdata(sd_bus_track *track) { - assert_return(track, NULL); - - return track->userdata; -} - -_public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) { - void *ret; - - assert_return(track, NULL); - - ret = track->userdata; - track->userdata = userdata; - - return ret; -} diff --git a/src/libsystemd/sd-bus/bus-track.h b/src/libsystemd/sd-bus/bus-track.h deleted file mode 100644 index 7d93a727d6..0000000000 --- a/src/libsystemd/sd-bus/bus-track.h +++ /dev/null @@ -1,22 +0,0 @@ -#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 . -***/ - -void bus_track_dispatch(sd_bus_track *track); diff --git a/src/libsystemd/sd-bus/bus-type.c b/src/libsystemd/sd-bus/bus-type.c deleted file mode 100644 index c692afc580..0000000000 --- a/src/libsystemd/sd-bus/bus-type.c +++ /dev/null @@ -1,176 +0,0 @@ -/*** - 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 . -***/ - -#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 deleted file mode 100644 index 5c87eb5f08..0000000000 --- a/src/libsystemd/sd-bus/bus-type.h +++ /dev/null @@ -1,37 +0,0 @@ -#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 . -***/ - -#include - -#include "sd-bus.h" - -#include "macro.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_; -/* "trivial" is systemd's term for what the D-Bus Specification calls - * a "fixed type": that is, a basic type of fixed length */ -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/busctl-introspect.c b/src/libsystemd/sd-bus/busctl-introspect.c deleted file mode 100644 index b09509f8e1..0000000000 --- a/src/libsystemd/sd-bus/busctl-introspect.c +++ /dev/null @@ -1,790 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 . -***/ - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "busctl-introspect.h" -#include "string-util.h" -#include "util.h" -#include "xml.h" - -#define NODE_DEPTH_MAX 16 - -typedef struct Context { - const XMLIntrospectOps *ops; - void *userdata; - - char *interface_name; - uint64_t interface_flags; - - char *member_name; - char *member_signature; - char *member_result; - uint64_t member_flags; - bool member_writable; - - const char *current; - void *xml_state; -} Context; - -static void context_reset_member(Context *c) { - free(c->member_name); - free(c->member_signature); - free(c->member_result); - - c->member_name = c->member_signature = c->member_result = NULL; - c->member_flags = 0; - c->member_writable = false; -} - -static void context_reset_interface(Context *c) { - c->interface_name = mfree(c->interface_name); - c->interface_flags = 0; - - context_reset_member(c); -} - -static int parse_xml_annotation(Context *context, uint64_t *flags) { - - enum { - STATE_ANNOTATION, - STATE_NAME, - STATE_VALUE - } state = STATE_ANNOTATION; - - _cleanup_free_ char *field = NULL, *value = NULL; - - assert(context); - - for (;;) { - _cleanup_free_ char *name = NULL; - - int t; - - t = xml_tokenize(&context->current, &name, &context->xml_state, NULL); - if (t < 0) { - log_error("XML parse error."); - return t; - } - - if (t == XML_END) { - log_error("Premature end of XML data."); - return -EBADMSG; - } - - switch (state) { - - case STATE_ANNOTATION: - - if (t == XML_ATTRIBUTE_NAME) { - - if (streq_ptr(name, "name")) - state = STATE_NAME; - - else if (streq_ptr(name, "value")) - state = STATE_VALUE; - - else { - log_error("Unexpected attribute %s.", name); - return -EBADMSG; - } - - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "annotation"))) { - - if (flags) { - if (streq_ptr(field, "org.freedesktop.DBus.Deprecated")) { - - if (streq_ptr(value, "true")) - *flags |= SD_BUS_VTABLE_DEPRECATED; - - } else if (streq_ptr(field, "org.freedesktop.DBus.Method.NoReply")) { - - if (streq_ptr(value, "true")) - *flags |= SD_BUS_VTABLE_METHOD_NO_REPLY; - - } else if (streq_ptr(field, "org.freedesktop.DBus.Property.EmitsChangedSignal")) { - - if (streq_ptr(value, "const")) - *flags = (*flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) | SD_BUS_VTABLE_PROPERTY_CONST; - else if (streq_ptr(value, "invalidates")) - *flags = (*flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_CONST)) | SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION; - else if (streq_ptr(value, "false")) - *flags = *flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION); - } - } - - return 0; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in . (1)"); - return -EINVAL; - } - - break; - - case STATE_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - free(field); - field = name; - name = NULL; - - state = STATE_ANNOTATION; - } else { - log_error("Unexpected token in . (2)"); - return -EINVAL; - } - - break; - - case STATE_VALUE: - - if (t == XML_ATTRIBUTE_VALUE) { - free(value); - value = name; - name = NULL; - - state = STATE_ANNOTATION; - } else { - log_error("Unexpected token in . (3)"); - return -EINVAL; - } - - break; - - default: - assert_not_reached("Bad state"); - } - } -} - -static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth) { - - enum { - STATE_NODE, - STATE_NODE_NAME, - STATE_INTERFACE, - STATE_INTERFACE_NAME, - STATE_METHOD, - STATE_METHOD_NAME, - STATE_METHOD_ARG, - STATE_METHOD_ARG_NAME, - STATE_METHOD_ARG_TYPE, - STATE_METHOD_ARG_DIRECTION, - STATE_SIGNAL, - STATE_SIGNAL_NAME, - STATE_SIGNAL_ARG, - STATE_SIGNAL_ARG_NAME, - STATE_SIGNAL_ARG_TYPE, - STATE_PROPERTY, - STATE_PROPERTY_NAME, - STATE_PROPERTY_TYPE, - STATE_PROPERTY_ACCESS, - } state = STATE_NODE; - - _cleanup_free_ char *node_path = NULL, *argument_type = NULL, *argument_direction = NULL; - const char *np = prefix; - int r; - - assert(context); - assert(prefix); - - if (n_depth > NODE_DEPTH_MAX) { - log_error(" depth too high."); - return -EINVAL; - } - - for (;;) { - _cleanup_free_ char *name = NULL; - int t; - - t = xml_tokenize(&context->current, &name, &context->xml_state, NULL); - if (t < 0) { - log_error("XML parse error."); - return t; - } - - if (t == XML_END) { - log_error("Premature end of XML data."); - return -EBADMSG; - } - - switch (state) { - - case STATE_NODE: - if (t == XML_ATTRIBUTE_NAME) { - - if (streq_ptr(name, "name")) - state = STATE_NODE_NAME; - else { - log_error("Unexpected attribute %s.", name); - return -EBADMSG; - } - - } else if (t == XML_TAG_OPEN) { - - if (streq_ptr(name, "interface")) - state = STATE_INTERFACE; - else if (streq_ptr(name, "node")) { - - r = parse_xml_node(context, np, n_depth+1); - if (r < 0) - return r; - } else { - log_error("Unexpected tag %s.", name); - return -EBADMSG; - } - - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "node"))) { - - if (context->ops->on_path) { - r = context->ops->on_path(node_path ? node_path : np, context->userdata); - if (r < 0) - return r; - } - - return 0; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in . (1)"); - return -EINVAL; - } - - break; - - case STATE_NODE_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - - free(node_path); - - if (name[0] == '/') { - node_path = name; - name = NULL; - } else { - - if (endswith(prefix, "/")) - node_path = strappend(prefix, name); - else - node_path = strjoin(prefix, "/", name, NULL); - if (!node_path) - return log_oom(); - } - - np = node_path; - state = STATE_NODE; - } else { - log_error("Unexpected token in . (2)"); - return -EINVAL; - } - - break; - - case STATE_INTERFACE: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq_ptr(name, "name")) - state = STATE_INTERFACE_NAME; - else { - log_error("Unexpected attribute %s.", name); - return -EBADMSG; - } - - } else if (t == XML_TAG_OPEN) { - if (streq_ptr(name, "method")) - state = STATE_METHOD; - else if (streq_ptr(name, "signal")) - state = STATE_SIGNAL; - else if (streq_ptr(name, "property")) { - context->member_flags |= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE; - state = STATE_PROPERTY; - } else if (streq_ptr(name, "annotation")) { - r = parse_xml_annotation(context, &context->interface_flags); - if (r < 0) - return r; - } else { - log_error("Unexpected tag %s.", name); - return -EINVAL; - } - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "interface"))) { - - if (n_depth == 0) { - if (context->ops->on_interface) { - r = context->ops->on_interface(context->interface_name, context->interface_flags, context->userdata); - if (r < 0) - return r; - } - - context_reset_interface(context); - } - - state = STATE_NODE; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in . (1)"); - return -EINVAL; - } - - break; - - case STATE_INTERFACE_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - if (n_depth == 0) { - free(context->interface_name); - context->interface_name = name; - name = NULL; - } - - state = STATE_INTERFACE; - } else { - log_error("Unexpected token in . (2)"); - return -EINVAL; - } - - break; - - case STATE_METHOD: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq_ptr(name, "name")) - state = STATE_METHOD_NAME; - else { - log_error("Unexpected attribute %s", name); - return -EBADMSG; - } - } else if (t == XML_TAG_OPEN) { - if (streq_ptr(name, "arg")) - state = STATE_METHOD_ARG; - else if (streq_ptr(name, "annotation")) { - r = parse_xml_annotation(context, &context->member_flags); - if (r < 0) - return r; - } else { - log_error("Unexpected tag %s.", name); - return -EINVAL; - } - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "method"))) { - - if (n_depth == 0) { - if (context->ops->on_method) { - r = context->ops->on_method(context->interface_name, context->member_name, context->member_signature, context->member_result, context->member_flags, context->userdata); - if (r < 0) - return r; - } - - context_reset_member(context); - } - - state = STATE_INTERFACE; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in (1)."); - return -EINVAL; - } - - break; - - case STATE_METHOD_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_name); - context->member_name = name; - name = NULL; - } - - state = STATE_METHOD; - } else { - log_error("Unexpected token in (2)."); - return -EINVAL; - } - - break; - - case STATE_METHOD_ARG: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq_ptr(name, "name")) - state = STATE_METHOD_ARG_NAME; - else if (streq_ptr(name, "type")) - state = STATE_METHOD_ARG_TYPE; - else if (streq_ptr(name, "direction")) - state = STATE_METHOD_ARG_DIRECTION; - else { - log_error("Unexpected method attribute %s.", name); - return -EBADMSG; - } - } else if (t == XML_TAG_OPEN) { - if (streq_ptr(name, "annotation")) { - r = parse_xml_annotation(context, NULL); - if (r < 0) - return r; - } else { - log_error("Unexpected method tag %s.", name); - return -EINVAL; - } - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) { - - if (n_depth == 0) { - - if (argument_type) { - if (!argument_direction || streq(argument_direction, "in")) { - if (!strextend(&context->member_signature, argument_type, NULL)) - return log_oom(); - } else if (streq(argument_direction, "out")) { - if (!strextend(&context->member_result, argument_type, NULL)) - return log_oom(); - } - } - - argument_type = mfree(argument_type); - argument_direction = mfree(argument_direction); - } - - state = STATE_METHOD; - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in method . (1)"); - return -EINVAL; - } - - break; - - case STATE_METHOD_ARG_NAME: - - if (t == XML_ATTRIBUTE_VALUE) - state = STATE_METHOD_ARG; - else { - log_error("Unexpected token in method . (2)"); - return -EINVAL; - } - - break; - - case STATE_METHOD_ARG_TYPE: - - if (t == XML_ATTRIBUTE_VALUE) { - free(argument_type); - argument_type = name; - name = NULL; - - state = STATE_METHOD_ARG; - } else { - log_error("Unexpected token in method . (3)"); - return -EINVAL; - } - - break; - - case STATE_METHOD_ARG_DIRECTION: - - if (t == XML_ATTRIBUTE_VALUE) { - free(argument_direction); - argument_direction = name; - name = NULL; - - state = STATE_METHOD_ARG; - } else { - log_error("Unexpected token in method . (4)"); - return -EINVAL; - } - - break; - - case STATE_SIGNAL: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq_ptr(name, "name")) - state = STATE_SIGNAL_NAME; - else { - log_error("Unexpected attribute %s.", name); - return -EBADMSG; - } - } else if (t == XML_TAG_OPEN) { - if (streq_ptr(name, "arg")) - state = STATE_SIGNAL_ARG; - else if (streq_ptr(name, "annotation")) { - r = parse_xml_annotation(context, &context->member_flags); - if (r < 0) - return r; - } else { - log_error("Unexpected tag %s.", name); - return -EINVAL; - } - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "signal"))) { - - if (n_depth == 0) { - if (context->ops->on_signal) { - r = context->ops->on_signal(context->interface_name, context->member_name, context->member_signature, context->member_flags, context->userdata); - if (r < 0) - return r; - } - - context_reset_member(context); - } - - state = STATE_INTERFACE; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in . (1)"); - return -EINVAL; - } - - break; - - case STATE_SIGNAL_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_name); - context->member_name = name; - name = NULL; - } - - state = STATE_SIGNAL; - } else { - log_error("Unexpected token in . (2)"); - return -EINVAL; - } - - break; - - - case STATE_SIGNAL_ARG: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq_ptr(name, "name")) - state = STATE_SIGNAL_ARG_NAME; - else if (streq_ptr(name, "type")) - state = STATE_SIGNAL_ARG_TYPE; - else { - log_error("Unexpected signal attribute %s.", name); - return -EBADMSG; - } - } else if (t == XML_TAG_OPEN) { - if (streq_ptr(name, "annotation")) { - r = parse_xml_annotation(context, NULL); - if (r < 0) - return r; - } else { - log_error("Unexpected signal tag %s.", name); - return -EINVAL; - } - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) { - - if (argument_type) { - if (!strextend(&context->member_signature, argument_type, NULL)) - return log_oom(); - - argument_type = mfree(argument_type); - } - - state = STATE_SIGNAL; - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in signal (1)."); - return -EINVAL; - } - - break; - - case STATE_SIGNAL_ARG_NAME: - - if (t == XML_ATTRIBUTE_VALUE) - state = STATE_SIGNAL_ARG; - else { - log_error("Unexpected token in signal (2)."); - return -EINVAL; - } - - break; - - case STATE_SIGNAL_ARG_TYPE: - - if (t == XML_ATTRIBUTE_VALUE) { - free(argument_type); - argument_type = name; - name = NULL; - - state = STATE_SIGNAL_ARG; - } else { - log_error("Unexpected token in signal (3)."); - return -EINVAL; - } - - break; - - case STATE_PROPERTY: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq_ptr(name, "name")) - state = STATE_PROPERTY_NAME; - else if (streq_ptr(name, "type")) - state = STATE_PROPERTY_TYPE; - else if (streq_ptr(name, "access")) - state = STATE_PROPERTY_ACCESS; - else { - log_error("Unexpected attribute %s.", name); - return -EBADMSG; - } - } else if (t == XML_TAG_OPEN) { - - if (streq_ptr(name, "annotation")) { - r = parse_xml_annotation(context, &context->member_flags); - if (r < 0) - return r; - } else { - log_error("Unexpected tag %s.", name); - return -EINVAL; - } - - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "property"))) { - - if (n_depth == 0) { - if (context->ops->on_property) { - r = context->ops->on_property(context->interface_name, context->member_name, context->member_signature, context->member_writable, context->member_flags, context->userdata); - if (r < 0) - return r; - } - - context_reset_member(context); - } - - state = STATE_INTERFACE; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in . (1)"); - return -EINVAL; - } - - break; - - case STATE_PROPERTY_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_name); - context->member_name = name; - name = NULL; - } - state = STATE_PROPERTY; - } else { - log_error("Unexpected token in . (2)"); - return -EINVAL; - } - - break; - - case STATE_PROPERTY_TYPE: - - if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_signature); - context->member_signature = name; - name = NULL; - } - - state = STATE_PROPERTY; - } else { - log_error("Unexpected token in . (3)"); - return -EINVAL; - } - - break; - - case STATE_PROPERTY_ACCESS: - - if (t == XML_ATTRIBUTE_VALUE) { - - if (streq(name, "readwrite") || streq(name, "write")) - context->member_writable = true; - - state = STATE_PROPERTY; - } else { - log_error("Unexpected token in . (4)"); - return -EINVAL; - } - - break; - } - } -} - -int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata) { - Context context = { - .ops = ops, - .userdata = userdata, - .current = xml, - }; - - int r; - - assert(prefix); - assert(xml); - assert(ops); - - for (;;) { - _cleanup_free_ char *name = NULL; - - r = xml_tokenize(&context.current, &name, &context.xml_state, NULL); - if (r < 0) { - log_error("XML parse error"); - goto finish; - } - - if (r == XML_END) { - r = 0; - break; - } - - if (r == XML_TAG_OPEN) { - - if (streq(name, "node")) { - r = parse_xml_node(&context, prefix, 0); - if (r < 0) - goto finish; - } else { - log_error("Unexpected tag '%s' in introspection data.", name); - r = -EBADMSG; - goto finish; - } - } else if (r != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token."); - r = -EBADMSG; - goto finish; - } - } - -finish: - context_reset_interface(&context); - - return r; -} diff --git a/src/libsystemd/sd-bus/busctl-introspect.h b/src/libsystemd/sd-bus/busctl-introspect.h deleted file mode 100644 index d922e352db..0000000000 --- a/src/libsystemd/sd-bus/busctl-introspect.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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 . -***/ - -#include - -typedef struct XMLIntrospectOps { - int (*on_path)(const char *path, void *userdata); - int (*on_interface)(const char *name, uint64_t flags, void *userdata); - int (*on_method)(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata); - int (*on_signal)(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata); - int (*on_property)(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata); -} XMLIntrospectOps; - -int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata); diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c deleted file mode 100644 index bfe967bfb0..0000000000 --- a/src/libsystemd/sd-bus/busctl.c +++ /dev/null @@ -1,2086 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-dump.h" -#include "bus-internal.h" -#include "bus-signature.h" -#include "bus-type.h" -#include "bus-util.h" -#include "busctl-introspect.h" -#include "escape.h" -#include "fd-util.h" -#include "locale-util.h" -#include "log.h" -#include "pager.h" -#include "parse-util.h" -#include "path-util.h" -#include "set.h" -#include "strv.h" -#include "terminal-util.h" -#include "user-util.h" -#include "util.h" - -static bool arg_no_pager = false; -static bool arg_legend = true; -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 size_t arg_snaplen = 4096; -static bool arg_list = false; -static bool arg_quiet = false; -static bool arg_verbose = false; -static bool arg_expect_reply = true; -static bool arg_auto_start = true; -static bool arg_allow_interactive_authorization = true; -static bool arg_augment_creds = true; -static usec_t arg_timeout = 0; - -#define NAME_IS_ACQUIRED INT_TO_PTR(1) -#define NAME_IS_ACTIVATABLE INT_TO_PTR(2) - -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); - - if (!arg_unique && !arg_acquired && !arg_activatable) - arg_unique = arg_acquired = arg_activatable = true; - - r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL); - if (r < 0) - return log_error_errno(r, "Failed to list names: %m"); - - pager_open(arg_no_pager, false); - - names = hashmap_new(&string_hash_ops); - if (!names) - return log_oom(); - - STRV_FOREACH(i, acquired) { - max_i = MAX(max_i, strlen(*i)); - - r = hashmap_put(names, *i, NAME_IS_ACQUIRED); - if (r < 0) - return log_error_errno(r, "Failed to add to hashmap: %m"); - } - - STRV_FOREACH(i, activatable) { - max_i = MAX(max_i, strlen(*i)); - - r = hashmap_put(names, *i, NAME_IS_ACTIVATABLE); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Failed to add to hashmap: %m"); - } - - merged = new(char*, hashmap_size(names) + 1); - HASHMAP_FOREACH_KEY(v, k, names, iterator) - merged[n++] = k; - - merged[n] = NULL; - strv_sort(merged); - - if (arg_legend) { - printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s", - (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION"); - - if (arg_show_machine) - puts(" MACHINE"); - else - putchar('\n'); - } - - STRV_FOREACH(i, merged) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - sd_id128_t mid; - - if (hashmap_get(names, *i) == NAME_IS_ACTIVATABLE) { - /* 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_name_creds( - bus, *i, - (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | - SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM| - SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION| - SD_BUS_CREDS_DESCRIPTION, &creds); - if (r >= 0) { - const char *unique, *session, *unit, *cn; - 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_euid(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); - - r = sd_bus_creds_get_description(creds, &cn); - if (r >= 0) - printf(" %-19s", cn); - else - fputs(" - ", stdout); - - } else - printf(" - - - - - - - "); - - if (arg_show_machine) { - r = sd_bus_get_name_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 void print_subtree(const char *prefix, const char *path, char **l) { - const char *vertical, *space; - char **n; - - /* We assume the list is sorted. Let's first skip over the - * entry we are looking at. */ - for (;;) { - if (!*l) - return; - - if (!streq(*l, path)) - break; - - l++; - } - - vertical = strjoina(prefix, special_glyph(TREE_VERTICAL)); - space = strjoina(prefix, special_glyph(TREE_SPACE)); - - for (;;) { - bool has_more = false; - - if (!*l || !path_startswith(*l, path)) - break; - - n = l + 1; - for (;;) { - if (!*n || !path_startswith(*n, path)) - break; - - if (!path_startswith(*n, *l)) { - has_more = true; - break; - } - - n++; - } - - printf("%s%s%s\n", prefix, special_glyph(has_more ? TREE_BRANCH : TREE_RIGHT), *l); - - print_subtree(has_more ? vertical : space, *l, l); - l = n; - } -} - -static void print_tree(const char *prefix, char **l) { - - pager_open(arg_no_pager, false); - - prefix = strempty(prefix); - - if (arg_list) { - char **i; - - STRV_FOREACH(i, l) - printf("%s%s\n", prefix, *i); - return; - } - - if (strv_isempty(l)) { - printf("No objects discovered.\n"); - return; - } - - if (streq(l[0], "/") && !l[1]) { - printf("Only root object discovered.\n"); - return; - } - - print_subtree(prefix, "/", l); -} - -static int on_path(const char *path, void *userdata) { - Set *paths = userdata; - int r; - - assert(paths); - - r = set_put_strdup(paths, path); - if (r < 0) - return log_oom(); - - return 0; -} - -static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths, bool many) { - static const XMLIntrospectOps ops = { - .on_path = on_path, - }; - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *xml; - int r; - - r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); - if (r < 0) { - if (many) - printf("Failed to introspect object %s of service %s: %s\n", path, service, bus_error_message(&error, r)); - else - log_error("Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_read(reply, "s", &xml); - if (r < 0) - return bus_log_parse_error(r); - - return parse_xml_introspect(path, xml, &ops, paths); -} - -static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool many) { - _cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL; - _cleanup_free_ char **l = NULL; - char *m; - int r; - - paths = set_new(&string_hash_ops); - if (!paths) - return log_oom(); - - done = set_new(&string_hash_ops); - if (!done) - return log_oom(); - - failed = set_new(&string_hash_ops); - if (!failed) - return log_oom(); - - m = strdup("/"); - if (!m) - return log_oom(); - - r = set_put(paths, m); - if (r < 0) { - free(m); - return log_oom(); - } - - for (;;) { - _cleanup_free_ char *p = NULL; - int q; - - p = set_steal_first(paths); - if (!p) - break; - - if (set_contains(done, p) || - set_contains(failed, p)) - continue; - - q = find_nodes(bus, service, p, paths, many); - if (q < 0) { - if (r >= 0) - r = q; - - q = set_put(failed, p); - } else - q = set_put(done, p); - - if (q < 0) - return log_oom(); - - assert(q != 0); - p = NULL; - } - - pager_open(arg_no_pager, false); - - l = set_get_strv(done); - if (!l) - return log_oom(); - - strv_sort(l); - print_tree(prefix, l); - - fflush(stdout); - - return r; -} - -static int tree(sd_bus *bus, char **argv) { - char **i; - int r = 0; - - if (!arg_unique && !arg_acquired) - arg_acquired = true; - - if (strv_length(argv) <= 1) { - _cleanup_strv_free_ char **names = NULL; - bool not_first = false; - - r = sd_bus_list_names(bus, &names, NULL); - if (r < 0) - return log_error_errno(r, "Failed to get name list: %m"); - - pager_open(arg_no_pager, false); - - STRV_FOREACH(i, names) { - int q; - - if (!arg_unique && (*i)[0] == ':') - continue; - - if (!arg_acquired && (*i)[0] == ':') - continue; - - if (not_first) - printf("\n"); - - printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal()); - - q = tree_one(bus, *i, NULL, true); - if (q < 0 && r >= 0) - r = q; - - not_first = true; - } - } else { - STRV_FOREACH(i, argv+1) { - int q; - - if (i > argv+1) - printf("\n"); - - if (argv[2]) { - pager_open(arg_no_pager, false); - printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal()); - } - - q = tree_one(bus, *i, NULL, !!argv[2]); - if (q < 0 && r >= 0) - r = q; - } - } - - return r; -} - -static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) { - int r; - - for (;;) { - 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) - return r; - if (r == 0) - return needs_space; - - if (bus_type_is_container(type) > 0) { - - r = sd_bus_message_enter_container(m, type, contents); - if (r < 0) - return r; - - if (type == SD_BUS_TYPE_ARRAY) { - unsigned n = 0; - - /* count array entries */ - for (;;) { - - r = sd_bus_message_skip(m, contents); - if (r < 0) - return r; - if (r == 0) - break; - - n++; - } - - r = sd_bus_message_rewind(m, false); - if (r < 0) - return r; - - if (needs_space) - fputc(' ', f); - - fprintf(f, "%u", n); - needs_space = true; - - } else if (type == SD_BUS_TYPE_VARIANT) { - - if (needs_space) - fputc(' ', f); - - fprintf(f, "%s", contents); - needs_space = true; - } - - r = format_cmdline(m, f, needs_space); - if (r < 0) - return r; - - needs_space = r > 0; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - continue; - } - - r = sd_bus_message_read_basic(m, type, &basic); - if (r < 0) - return r; - - if (needs_space) - fputc(' ', f); - - switch (type) { - case SD_BUS_TYPE_BYTE: - fprintf(f, "%u", basic.u8); - break; - - case SD_BUS_TYPE_BOOLEAN: - fputs(true_false(basic.i), f); - break; - - case SD_BUS_TYPE_INT16: - fprintf(f, "%i", basic.s16); - break; - - case SD_BUS_TYPE_UINT16: - fprintf(f, "%u", basic.u16); - break; - - case SD_BUS_TYPE_INT32: - fprintf(f, "%i", basic.s32); - break; - - case SD_BUS_TYPE_UINT32: - fprintf(f, "%u", basic.u32); - break; - - case SD_BUS_TYPE_INT64: - fprintf(f, "%" PRIi64, basic.s64); - break; - - case SD_BUS_TYPE_UINT64: - fprintf(f, "%" PRIu64, basic.u64); - break; - - case SD_BUS_TYPE_DOUBLE: - fprintf(f, "%g", basic.d64); - break; - - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: - case SD_BUS_TYPE_SIGNATURE: { - _cleanup_free_ char *b = NULL; - - b = cescape(basic.string); - if (!b) - return -ENOMEM; - - fprintf(f, "\"%s\"", b); - break; - } - - case SD_BUS_TYPE_UNIX_FD: - fprintf(f, "%i", basic.i); - break; - - default: - assert_not_reached("Unknown basic type."); - } - - needs_space = true; - } -} - -typedef struct Member { - const char *type; - char *interface; - char *name; - char *signature; - char *result; - char *value; - bool writable; - uint64_t flags; -} Member; - -static void member_hash_func(const void *p, struct siphash *state) { - const Member *m = p; - uint64_t arity = 1; - - assert(m); - assert(m->type); - - string_hash_func(m->type, state); - - arity += !!m->name + !!m->interface; - - uint64_hash_func(&arity, state); - - if (m->name) - string_hash_func(m->name, state); - - if (m->interface) - string_hash_func(m->interface, state); -} - -static int member_compare_func(const void *a, const void *b) { - const Member *x = a, *y = b; - int d; - - assert(x); - assert(y); - assert(x->type); - assert(y->type); - - d = strcmp_ptr(x->interface, y->interface); - if (d != 0) - return d; - - d = strcmp(x->type, y->type); - if (d != 0) - return d; - - return strcmp_ptr(x->name, y->name); -} - -static int member_compare_funcp(const void *a, const void *b) { - const Member *const * x = (const Member *const *) a, * const *y = (const Member *const *) b; - - return member_compare_func(*x, *y); -} - -static void member_free(Member *m) { - if (!m) - return; - - free(m->interface); - free(m->name); - free(m->signature); - free(m->result); - free(m->value); - free(m); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free); - -static void member_set_free(Set *s) { - Member *m; - - while ((m = set_steal_first(s))) - member_free(m); - - set_free(s); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free); - -static int on_interface(const char *interface, uint64_t flags, void *userdata) { - _cleanup_(member_freep) Member *m; - Set *members = userdata; - int r; - - assert(interface); - assert(members); - - m = new0(Member, 1); - if (!m) - return log_oom(); - - m->type = "interface"; - m->flags = flags; - - r = free_and_strdup(&m->interface, interface); - if (r < 0) - return log_oom(); - - r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate interface"); - return -EINVAL; - } - - m = NULL; - return 0; -} - -static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) { - _cleanup_(member_freep) Member *m; - Set *members = userdata; - int r; - - assert(interface); - assert(name); - - m = new0(Member, 1); - if (!m) - return log_oom(); - - m->type = "method"; - m->flags = flags; - - r = free_and_strdup(&m->interface, interface); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->name, name); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->signature, signature); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->result, result); - if (r < 0) - return log_oom(); - - r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate method"); - return -EINVAL; - } - - m = NULL; - return 0; -} - -static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) { - _cleanup_(member_freep) Member *m; - Set *members = userdata; - int r; - - assert(interface); - assert(name); - - m = new0(Member, 1); - if (!m) - return log_oom(); - - m->type = "signal"; - m->flags = flags; - - r = free_and_strdup(&m->interface, interface); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->name, name); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->signature, signature); - if (r < 0) - return log_oom(); - - r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate signal"); - return -EINVAL; - } - - m = NULL; - return 0; -} - -static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) { - _cleanup_(member_freep) Member *m; - Set *members = userdata; - int r; - - assert(interface); - assert(name); - - m = new0(Member, 1); - if (!m) - return log_oom(); - - m->type = "property"; - m->flags = flags; - m->writable = writable; - - r = free_and_strdup(&m->interface, interface); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->name, name); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->signature, signature); - if (r < 0) - return log_oom(); - - r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate property"); - return -EINVAL; - } - - m = NULL; - return 0; -} - -static const char *strdash(const char *x) { - return isempty(x) ? "-" : x; -} - -static int introspect(sd_bus *bus, char **argv) { - static const struct hash_ops member_hash_ops = { - .hash = member_hash_func, - .compare = member_compare_func, - }; - - static const XMLIntrospectOps ops = { - .on_interface = on_interface, - .on_method = on_method, - .on_signal = on_signal, - .on_property = on_property, - }; - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(member_set_freep) Set *members = NULL; - Iterator i; - Member *m; - const char *xml; - int r; - unsigned name_width, type_width, signature_width, result_width; - Member **sorted = NULL; - unsigned k = 0, j, n_args; - - n_args = strv_length(argv); - if (n_args < 3) { - log_error("Requires service and object path argument."); - return -EINVAL; - } - - if (n_args > 4) { - log_error("Too many arguments."); - return -EINVAL; - } - - members = set_new(&member_hash_ops); - if (!members) - return log_oom(); - - r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); - if (r < 0) { - log_error("Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_read(reply, "s", &xml); - if (r < 0) - return bus_log_parse_error(r); - - /* First, get list of all properties */ - r = parse_xml_introspect(argv[2], xml, &ops, members); - if (r < 0) - return r; - - /* Second, find the current values for them */ - SET_FOREACH(m, members, i) { - - if (!streq(m->type, "property")) - continue; - - if (m->value) - continue; - - if (argv[3] && !streq(argv[3], m->interface)) - continue; - - r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface); - if (r < 0) { - log_error("%s", bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_enter_container(reply, 'a', "{sv}"); - if (r < 0) - return bus_log_parse_error(r); - - for (;;) { - Member *z; - _cleanup_free_ char *buf = NULL; - _cleanup_fclose_ FILE *mf = NULL; - size_t sz = 0; - const char *name; - - r = sd_bus_message_enter_container(reply, 'e', "sv"); - if (r < 0) - return bus_log_parse_error(r); - - if (r == 0) - break; - - r = sd_bus_message_read(reply, "s", &name); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_enter_container(reply, 'v', NULL); - if (r < 0) - return bus_log_parse_error(r); - - mf = open_memstream(&buf, &sz); - if (!mf) - return log_oom(); - - r = format_cmdline(reply, mf, false); - if (r < 0) - return bus_log_parse_error(r); - - fclose(mf); - mf = NULL; - - z = set_get(members, &((Member) { - .type = "property", - .interface = m->interface, - .name = (char*) name })); - if (z) { - free(z->value); - z->value = buf; - buf = NULL; - } - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - } - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - } - - pager_open(arg_no_pager, false); - - name_width = strlen("NAME"); - type_width = strlen("TYPE"); - signature_width = strlen("SIGNATURE"); - result_width = strlen("RESULT/VALUE"); - - sorted = newa(Member*, set_size(members)); - - SET_FOREACH(m, members, i) { - - if (argv[3] && !streq(argv[3], m->interface)) - continue; - - if (m->interface) - name_width = MAX(name_width, strlen(m->interface)); - if (m->name) - name_width = MAX(name_width, strlen(m->name) + 1); - if (m->type) - type_width = MAX(type_width, strlen(m->type)); - if (m->signature) - signature_width = MAX(signature_width, strlen(m->signature)); - if (m->result) - result_width = MAX(result_width, strlen(m->result)); - if (m->value) - result_width = MAX(result_width, strlen(m->value)); - - sorted[k++] = m; - } - - if (result_width > 40) - result_width = 40; - - qsort(sorted, k, sizeof(Member*), member_compare_funcp); - - if (arg_legend) { - printf("%-*s %-*s %-*s %-*s %s\n", - (int) name_width, "NAME", - (int) type_width, "TYPE", - (int) signature_width, "SIGNATURE", - (int) result_width, "RESULT/VALUE", - "FLAGS"); - } - - for (j = 0; j < k; j++) { - _cleanup_free_ char *ellipsized = NULL; - const char *rv; - bool is_interface; - - m = sorted[j]; - - if (argv[3] && !streq(argv[3], m->interface)) - continue; - - is_interface = streq(m->type, "interface"); - - if (argv[3] && is_interface) - continue; - - if (m->value) { - ellipsized = ellipsize(m->value, result_width, 100); - if (!ellipsized) - return log_oom(); - - rv = ellipsized; - } else - rv = strdash(m->result); - - printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n", - is_interface ? ansi_highlight() : "", - is_interface ? "" : ".", - - !is_interface + (int) name_width, strdash(streq_ptr(m->type, "interface") ? m->interface : m->name), - is_interface ? ansi_normal() : "", - (int) type_width, strdash(m->type), - (int) signature_width, strdash(m->signature), - (int) result_width, rv, - (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"), - (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "", - (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "", - (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "", - (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "", - m->writable ? " writable" : ""); - } - - return 0; -} - -static int message_dump(sd_bus_message *m, FILE *f) { - return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER); -} - -static int message_pcap(sd_bus_message *m, FILE *f) { - return bus_message_pcap_frame(m, arg_snaplen, f); -} - -static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char **i; - uint32_t flags = 0; - int r; - - /* upgrade connection; it's not used for anything else after this call */ - r = sd_bus_message_new_method_call(bus, &message, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus.Monitoring", "BecomeMonitor"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(message, 'a', "s"); - if (r < 0) - return bus_log_create_error(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_message_append_basic(message, 's', m); - if (r < 0) - return bus_log_create_error(r); - - free(m); - m = strjoin("destination='", *i, "'", NULL); - if (!m) - return log_oom(); - - r = sd_bus_message_append_basic(message, 's', m); - if (r < 0) - return bus_log_create_error(r); - } - - STRV_FOREACH(i, arg_matches) { - r = sd_bus_message_append_basic(message, 's', *i); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(message); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(message, 'u', &flags); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_call(bus, message, arg_timeout, &error, NULL); - if (r < 0) { - log_error("%s", bus_error_message(&error, r)); - return r; - } - - log_info("Monitoring bus message stream."); - - for (;;) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - - r = sd_bus_process(bus, &m); - if (r < 0) - return log_error_errno(r, "Failed to process bus: %m"); - - if (m) { - dump(m, stdout); - fflush(stdout); - - if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected") > 0) { - log_info("Connection terminated, exiting."); - return 0; - } - - continue; - } - - if (r > 0) - continue; - - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) - return log_error_errno(r, "Failed to wait for bus: %m"); - } -} - -static int capture(sd_bus *bus, char *argv[]) { - int r; - - if (isatty(fileno(stdout)) > 0) { - log_error("Refusing to write message data to console, please redirect output to a file."); - return -EINVAL; - } - - bus_pcap_header(arg_snaplen, stdout); - - r = monitor(bus, argv, message_pcap); - if (r < 0) - return r; - - if (ferror(stdout)) { - log_error("Couldn't write capture file."); - return -EIO; - } - - return r; -} - -static int status(sd_bus *bus, char *argv[]) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - pid_t pid; - int r; - - assert(bus); - - if (strv_length(argv) > 2) { - log_error("Expects no or one argument."); - return -EINVAL; - } - - if (argv[1]) { - r = parse_pid(argv[1], &pid); - if (r < 0) - r = sd_bus_get_name_creds( - bus, - argv[1], - (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL, - &creds); - else - r = sd_bus_creds_new_from_pid( - &creds, - pid, - _SD_BUS_CREDS_ALL); - } else { - const char *scope, *address; - sd_id128_t bus_id; - - r = sd_bus_get_address(bus, &address); - if (r >= 0) - printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_normal()); - - r = sd_bus_get_scope(bus, &scope); - if (r >= 0) - printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_normal()); - - r = sd_bus_get_bus_id(bus, &bus_id); - if (r >= 0) - printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal()); - - r = sd_bus_get_owner_creds( - bus, - (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL, - &creds); - } - - if (r < 0) - return log_error_errno(r, "Failed to get credentials: %m"); - - bus_creds_dump(creds, NULL, false); - return 0; -} - -static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) { - char **p; - int r; - - assert(m); - assert(signature); - assert(x); - - p = *x; - - for (;;) { - const char *v; - char t; - - t = *signature; - v = *p; - - if (t == 0) - break; - if (!v) { - log_error("Too few parameters for signature."); - return -EINVAL; - } - - signature++; - p++; - - switch (t) { - - case SD_BUS_TYPE_BOOLEAN: - - r = parse_boolean(v); - if (r < 0) { - log_error("Failed to parse as boolean: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &r); - break; - - case SD_BUS_TYPE_BYTE: { - uint8_t z; - - r = safe_atou8(v, &z); - if (r < 0) { - log_error("Failed to parse as byte (unsigned 8bit integer): %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_INT16: { - int16_t z; - - r = safe_atoi16(v, &z); - if (r < 0) { - log_error("Failed to parse as signed 16bit integer: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_UINT16: { - uint16_t z; - - r = safe_atou16(v, &z); - if (r < 0) { - log_error("Failed to parse as unsigned 16bit integer: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_INT32: { - int32_t z; - - r = safe_atoi32(v, &z); - if (r < 0) { - log_error("Failed to parse as signed 32bit integer: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_UINT32: { - uint32_t z; - - r = safe_atou32(v, &z); - if (r < 0) { - log_error("Failed to parse as unsigned 32bit integer: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_INT64: { - int64_t z; - - r = safe_atoi64(v, &z); - if (r < 0) { - log_error("Failed to parse as signed 64bit integer: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_UINT64: { - uint64_t z; - - r = safe_atou64(v, &z); - if (r < 0) { - log_error("Failed to parse as unsigned 64bit integer: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - - case SD_BUS_TYPE_DOUBLE: { - double z; - - r = safe_atod(v, &z); - if (r < 0) { - log_error("Failed to parse as double precision floating point: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: - case SD_BUS_TYPE_SIGNATURE: - - r = sd_bus_message_append_basic(m, t, v); - break; - - case SD_BUS_TYPE_ARRAY: { - uint32_t n; - size_t k; - - r = safe_atou32(v, &n); - if (r < 0) { - log_error("Failed to parse number of array entries: %s", v); - return r; - } - - r = signature_element_length(signature, &k); - if (r < 0) { - log_error("Invalid array signature."); - return r; - } - - { - unsigned i; - char s[k + 1]; - memcpy(s, signature, k); - s[k] = 0; - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s); - if (r < 0) - return bus_log_create_error(r); - - for (i = 0; i < n; i++) { - r = message_append_cmdline(m, s, &p); - if (r < 0) - return r; - } - } - - signature += k; - - r = sd_bus_message_close_container(m); - break; - } - - case SD_BUS_TYPE_VARIANT: - r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v); - if (r < 0) - return bus_log_create_error(r); - - r = message_append_cmdline(m, v, &p); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - break; - - case SD_BUS_TYPE_STRUCT_BEGIN: - case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { - size_t k; - - signature--; - p--; - - r = signature_element_length(signature, &k); - if (r < 0) { - log_error("Invalid struct/dict entry signature."); - return r; - } - - { - char s[k-1]; - memcpy(s, signature + 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 bus_log_create_error(r); - - r = message_append_cmdline(m, s, &p); - if (r < 0) - return r; - } - - signature += k; - - r = sd_bus_message_close_container(m); - break; - } - - case SD_BUS_TYPE_UNIX_FD: - log_error("UNIX file descriptor not supported as type."); - return -EINVAL; - - default: - log_error("Unknown signature type %c.", t); - return -EINVAL; - } - - if (r < 0) - return bus_log_create_error(r); - } - - *x = p; - return 0; -} - -static int call(sd_bus *bus, char *argv[]) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; - int r; - - assert(bus); - - if (strv_length(argv) < 5) { - log_error("Expects at least four arguments."); - return -EINVAL; - } - - r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_set_expect_reply(m, arg_expect_reply); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_set_auto_start(m, arg_auto_start); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization); - if (r < 0) - return bus_log_create_error(r); - - if (!isempty(argv[5])) { - char **p; - - p = argv+6; - - r = message_append_cmdline(m, argv[5], &p); - if (r < 0) - return r; - - if (*p) { - log_error("Too many parameters for signature."); - return -EINVAL; - } - } - - if (!arg_expect_reply) { - r = sd_bus_send(bus, m, NULL); - if (r < 0) { - log_error("Failed to send message."); - return r; - } - - return 0; - } - - r = sd_bus_call(bus, m, arg_timeout, &error, &reply); - if (r < 0) { - log_error("%s", bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_is_empty(reply); - if (r < 0) - return bus_log_parse_error(r); - - if (r == 0 && !arg_quiet) { - - if (arg_verbose) { - pager_open(arg_no_pager, false); - - r = bus_message_dump(reply, stdout, 0); - if (r < 0) - return r; - } else { - - fputs(sd_bus_message_get_signature(reply, true), stdout); - fputc(' ', stdout); - - r = format_cmdline(reply, stdout, false); - if (r < 0) - return bus_log_parse_error(r); - - fputc('\n', stdout); - } - } - - return 0; -} - -static int get_property(sd_bus *bus, char *argv[]) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - unsigned n; - char **i; - int r; - - assert(bus); - - n = strv_length(argv); - if (n < 5) { - log_error("Expects at least four arguments."); - return -EINVAL; - } - - STRV_FOREACH(i, argv + 4) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *contents = NULL; - char type; - - r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i); - if (r < 0) { - log_error("%s", bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_peek_type(reply, &type, &contents); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_enter_container(reply, 'v', contents); - if (r < 0) - return bus_log_parse_error(r); - - if (arg_verbose) { - pager_open(arg_no_pager, false); - - r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY); - if (r < 0) - return r; - } else { - fputs(contents, stdout); - fputc(' ', stdout); - - r = format_cmdline(reply, stdout, false); - if (r < 0) - return bus_log_parse_error(r); - - fputc('\n', stdout); - } - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - } - - return 0; -} - -static int set_property(sd_bus *bus, char *argv[]) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - unsigned n; - char **p; - int r; - - assert(bus); - - n = strv_length(argv); - if (n < 6) { - log_error("Expects at least five arguments."); - return -EINVAL; - } - - r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "ss", argv[3], argv[4]); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', argv[5]); - if (r < 0) - return bus_log_create_error(r); - - p = argv+6; - r = message_append_cmdline(m, argv[5], &p); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - if (*p) { - log_error("Too many parameters for signature."); - return -EINVAL; - } - - r = sd_bus_call(bus, m, arg_timeout, &error, NULL); - if (r < 0) { - log_error("%s", bus_error_message(&error, r)); - return r; - } - - 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" - " --no-legend Do not show the headers and footers\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" - " --size=SIZE Maximum length of captured packet\n" - " --list Don't show tree, but simple object path list\n" - " --quiet Don't show method call reply\n" - " --verbose Show result values in long format\n" - " --expect-reply=BOOL Expect a method call reply\n" - " --auto-start=BOOL Auto-start destination service\n" - " --allow-interactive-authorization=BOOL\n" - " Allow interactive authorization for operation\n" - " --timeout=SECS Maximum time to wait for method call completion\n" - " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n\n" - "Commands:\n" - " list List bus names\n" - " status [SERVICE] Show bus service, process or bus owner credentials\n" - " monitor [SERVICE...] Show bus traffic\n" - " capture [SERVICE...] Capture bus traffic as pcap\n" - " tree [SERVICE...] Show object tree of service\n" - " introspect SERVICE OBJECT [INTERFACE]\n" - " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n" - " Call a method\n" - " get-property SERVICE OBJECT INTERFACE PROPERTY...\n" - " Get property value\n" - " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n" - " Set property value\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_NO_LEGEND, - ARG_SYSTEM, - ARG_USER, - ARG_ADDRESS, - ARG_MATCH, - ARG_SHOW_MACHINE, - ARG_UNIQUE, - ARG_ACQUIRED, - ARG_ACTIVATABLE, - ARG_SIZE, - ARG_LIST, - ARG_VERBOSE, - ARG_EXPECT_REPLY, - ARG_AUTO_START, - ARG_ALLOW_INTERACTIVE_AUTHORIZATION, - ARG_TIMEOUT, - ARG_AUGMENT_CREDS, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, - { "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' }, - { "size", required_argument, NULL, ARG_SIZE }, - { "list", no_argument, NULL, ARG_LIST }, - { "quiet", no_argument, NULL, 'q' }, - { "verbose", no_argument, NULL, ARG_VERBOSE }, - { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY }, - { "auto-start", required_argument, NULL, ARG_AUTO_START }, - { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION }, - { "timeout", required_argument, NULL, ARG_TIMEOUT }, - { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS}, - {}, - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0) - - switch (c) { - - case 'h': - return help(); - - case ARG_VERSION: - return version(); - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case ARG_NO_LEGEND: - arg_legend = false; - 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 ARG_SIZE: { - uint64_t sz; - - r = parse_size(optarg, 1024, &sz); - if (r < 0) { - log_error("Failed to parse size: %s", optarg); - return r; - } - - if ((uint64_t) (size_t) sz != sz) { - log_error("Size out of range."); - return -E2BIG; - } - - arg_snaplen = (size_t) sz; - break; - } - - case ARG_LIST: - arg_list = true; - break; - - case 'H': - arg_transport = BUS_TRANSPORT_REMOTE; - arg_host = optarg; - break; - - case 'M': - arg_transport = BUS_TRANSPORT_MACHINE; - arg_host = optarg; - break; - - case 'q': - arg_quiet = true; - break; - - case ARG_VERBOSE: - arg_verbose = true; - break; - - case ARG_EXPECT_REPLY: - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --expect-reply= parameter."); - return r; - } - - arg_expect_reply = !!r; - break; - - - case ARG_AUTO_START: - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --auto-start= parameter."); - return r; - } - - arg_auto_start = !!r; - break; - - - case ARG_ALLOW_INTERACTIVE_AUTHORIZATION: - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --allow-interactive-authorization= parameter."); - return r; - } - - arg_allow_interactive_authorization = !!r; - break; - - case ARG_TIMEOUT: - r = parse_sec(optarg, &arg_timeout); - if (r < 0) { - log_error("Failed to parse --timeout= parameter."); - return r; - } - - break; - - case ARG_AUGMENT_CREDS: - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --augment-creds= parameter."); - return r; - } - - arg_augment_creds = !!r; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - 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, message_dump); - - if (streq(argv[optind], "capture")) - return capture(bus, argv + optind); - - if (streq(argv[optind], "status")) - return status(bus, argv + optind); - - if (streq(argv[optind], "tree")) - return tree(bus, argv + optind); - - if (streq(argv[optind], "introspect")) - return introspect(bus, argv + optind); - - if (streq(argv[optind], "call")) - return call(bus, argv + optind); - - if (streq(argv[optind], "get-property")) - return get_property(bus, argv + optind); - - if (streq(argv[optind], "set-property")) - return set_property(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_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - r = sd_bus_new(&bus); - if (r < 0) { - log_error_errno(r, "Failed to allocate bus: %m"); - goto finish; - } - - if (streq_ptr(argv[optind], "monitor") || - streq_ptr(argv[optind], "capture")) { - - r = sd_bus_set_monitor(bus, true); - if (r < 0) { - log_error_errno(r, "Failed to set monitor mode: %m"); - goto finish; - } - - r = sd_bus_negotiate_creds(bus, true, _SD_BUS_CREDS_ALL); - if (r < 0) { - log_error_errno(r, "Failed to enable credentials: %m"); - goto finish; - } - - r = sd_bus_negotiate_timestamp(bus, true); - if (r < 0) { - log_error_errno(r, "Failed to enable timestamps: %m"); - goto finish; - } - - r = sd_bus_negotiate_fds(bus, true); - if (r < 0) { - log_error_errno(r, "Failed to enable fds: %m"); - goto finish; - } - } - - r = sd_bus_set_bus_client(bus, true); - if (r < 0) { - log_error_errno(r, "Failed to set bus client: %m"); - goto finish; - } - - if (arg_address) - r = sd_bus_set_address(bus, arg_address); - else { - switch (arg_transport) { - - case BUS_TRANSPORT_LOCAL: - if (arg_user) { - bus->is_user = true; - r = bus_set_address_user(bus); - } else { - bus->is_system = true; - r = bus_set_address_system(bus); - } - break; - - case BUS_TRANSPORT_REMOTE: - r = bus_set_address_system_remote(bus, arg_host); - break; - - case BUS_TRANSPORT_MACHINE: - r = bus_set_address_system_machine(bus, arg_host); - break; - - default: - assert_not_reached("Hmm, unknown transport type."); - } - } - if (r < 0) { - log_error_errno(r, "Failed to set address: %m"); - goto finish; - } - - r = sd_bus_start(bus); - if (r < 0) { - log_error_errno(r, "Failed to connect to bus: %m"); - 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 deleted file mode 100644 index ecffc6b13c..0000000000 --- a/src/libsystemd/sd-bus/kdbus.h +++ /dev/null @@ -1,980 +0,0 @@ -/* - * 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. - */ - -#ifndef _UAPI_KDBUS_H_ -#define _UAPI_KDBUS_H_ - -#include -#include - -#define KDBUS_IOCTL_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) -#define KDBUS_FLAG_NEGOTIATE (1ULL << 63) - -/** - * 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; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_notify_name_change - name registry change message - * @old_id: ID and flags of former owner of a name - * @new_id: 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_id; - struct kdbus_notify_id_change new_id; - char name[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_creds - process credentials - * @uid: User ID - * @euid: Effective UID - * @suid: Saved UID - * @fsuid: Filesystem UID - * @gid: Group ID - * @egid: Effective GID - * @sgid: Saved GID - * @fsgid: Filesystem GID - * - * Attached to: - * KDBUS_ITEM_CREDS - */ -struct kdbus_creds { - __u64 uid; - __u64 euid; - __u64 suid; - __u64 fsuid; - __u64 gid; - __u64 egid; - __u64 sgid; - __u64 fsgid; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_pids - process identifiers - * @pid: Process ID - * @tid: Thread ID - * @ppid: Parent process ID - * - * The PID and TID of a process. - * - * Attached to: - * KDBUS_ITEM_PIDS - */ -struct kdbus_pids { - __u64 pid; - __u64 tid; - __u64 ppid; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_caps - process capabilities - * @last_cap: Highest currently known capability bit - * @caps: Variable number of 32-bit capabilities flags - * - * Contains a variable number of 32-bit capabilities flags. - * - * Attached to: - * KDBUS_ITEM_CAPS - */ -struct kdbus_caps { - __u32 last_cap; - __u32 caps[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_audit - audit information - * @sessionid: The audit session ID - * @loginuid: The audit login uid - * - * Attached to: - * KDBUS_ITEM_AUDIT - */ -struct kdbus_audit { - __u32 sessionid; - __u32 loginuid; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_timestamp - * @seqnum: Global per-domain 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; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_vec - I/O vector for kdbus payload items - * @size: The size of the vector - * @address: Memory address of data buffer - * @offset: Offset in the in-message payload memory, - * relative to the message head - * - * Attached to: - * KDBUS_ITEM_PAYLOAD_VEC, KDBUS_ITEM_PAYLOAD_OFF - */ -struct kdbus_vec { - __u64 size; - union { - __u64 address; - __u64 offset; - }; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_bloom_parameter - bus-wide bloom parameters - * @size: Size of the bit field in bytes (m / 8) - * @n_hash: Number of hash functions used (k) - */ -struct kdbus_bloom_parameter { - __u64 size; - __u64 n_hash; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_bloom_filter - bloom filter containing n elements - * @generation: Generation of the element set in the filter - * @data: Bit field, multiple of 8 bytes - */ -struct kdbus_bloom_filter { - __u64 generation; - __u64 data[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_memfd - a kdbus memfd - * @start: The offset into the memfd where the segment starts - * @size: The size of the memfd segment - * @fd: The file descriptor number - * @__pad: Padding to ensure proper alignment and size - * - * Attached to: - * KDBUS_ITEM_PAYLOAD_MEMFD - */ -struct kdbus_memfd { - __u64 start; - __u64 size; - int fd; - __u32 __pad; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_name - a registered well-known name with its flags - * @flags: Flags from KDBUS_NAME_* - * @name: Well-known name - * - * Attached to: - * KDBUS_ITEM_OWNED_NAME - */ -struct kdbus_name { - __u64 flags; - char name[0]; -} __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_OWN: Allow to own a well-known name - * Implies KDBUS_POLICY_TALK and KDBUS_POLICY_SEE - * @KDBUS_POLICY_TALK: Allow communication to a well-known name - * Implies KDBUS_POLICY_SEE - * @KDBUS_POLICY_SEE: Allow to see a well-known name - */ -enum kdbus_policy_type { - KDBUS_POLICY_SEE = 0, - KDBUS_POLICY_TALK, - KDBUS_POLICY_OWN, -}; - -/** - * struct kdbus_policy_access - policy access item - * @type: One of KDBUS_POLICY_ACCESS_* types - * @access: Access to grant - * @id: For KDBUS_POLICY_ACCESS_USER, the uid - * For KDBUS_POLICY_ACCESS_GROUP, the gid - */ -struct kdbus_policy_access { - __u64 type; /* USER, GROUP, WORLD */ - __u64 access; /* OWN, TALK, SEE */ - __u64 id; /* uid, gid, 0 */ -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_attach_flags - flags for metadata attachments - * @KDBUS_ATTACH_TIMESTAMP: Timestamp - * @KDBUS_ATTACH_CREDS: Credentials - * @KDBUS_ATTACH_PIDS: PIDs - * @KDBUS_ATTACH_AUXGROUPS: Auxiliary groups - * @KDBUS_ATTACH_NAMES: Well-known names - * @KDBUS_ATTACH_TID_COMM: The "comm" process identifier of the TID - * @KDBUS_ATTACH_PID_COMM: The "comm" process identifier of the PID - * @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_DESCRIPTION: The human-readable connection name - * @_KDBUS_ATTACH_ALL: All of the above - * @_KDBUS_ATTACH_ANY: Wildcard match to enable any kind of - * metatdata. - */ -enum kdbus_attach_flags { - KDBUS_ATTACH_TIMESTAMP = 1ULL << 0, - KDBUS_ATTACH_CREDS = 1ULL << 1, - KDBUS_ATTACH_PIDS = 1ULL << 2, - KDBUS_ATTACH_AUXGROUPS = 1ULL << 3, - KDBUS_ATTACH_NAMES = 1ULL << 4, - KDBUS_ATTACH_TID_COMM = 1ULL << 5, - KDBUS_ATTACH_PID_COMM = 1ULL << 6, - KDBUS_ATTACH_EXE = 1ULL << 7, - KDBUS_ATTACH_CMDLINE = 1ULL << 8, - KDBUS_ATTACH_CGROUP = 1ULL << 9, - KDBUS_ATTACH_CAPS = 1ULL << 10, - KDBUS_ATTACH_SECLABEL = 1ULL << 11, - KDBUS_ATTACH_AUDIT = 1ULL << 12, - KDBUS_ATTACH_CONN_DESCRIPTION = 1ULL << 13, - _KDBUS_ATTACH_ALL = (1ULL << 14) - 1, - _KDBUS_ATTACH_ANY = ~0ULL -}; - -/** - * 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_NEGOTIATE: Negotiate supported 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_CANCEL_FD: FD used to cancel a synchronous - * operation by writing to it from - * userspace - * @KDBUS_ITEM_BLOOM_PARAMETER: Bus-wide bloom parameters, used with - * KDBUS_CMD_BUS_MAKE, carries a - * struct kdbus_bloom_parameter - * @KDBUS_ITEM_BLOOM_FILTER: Bloom filter carried with a message, - * used to match against a bloom mask of a - * connection, carries a struct - * kdbus_bloom_filter - * @KDBUS_ITEM_BLOOM_MASK: Bloom mask used to match against a - * message'sbloom filter - * @KDBUS_ITEM_DST_NAME: Destination's well-known name - * @KDBUS_ITEM_MAKE_NAME: Name of domain, bus, endpoint - * @KDBUS_ITEM_ATTACH_FLAGS_SEND: Attach-flags, used for updating which - * metadata a connection opts in to send - * @KDBUS_ITEM_ATTACH_FLAGS_RECV: Attach-flags, used for updating which - * metadata a connection requests to - * receive for each reeceived message - * @KDBUS_ITEM_ID: Connection ID - * @KDBUS_ITEM_NAME: Well-know name with flags - * @_KDBUS_ITEM_ATTACH_BASE: Start of metadata attach items - * @KDBUS_ITEM_TIMESTAMP: Timestamp - * @KDBUS_ITEM_CREDS: Process credentials - * @KDBUS_ITEM_PIDS: Process identifiers - * @KDBUS_ITEM_AUXGROUPS: Auxiliary process groups - * @KDBUS_ITEM_OWNED_NAME: A name owned by the associated - * connection - * @KDBUS_ITEM_TID_COMM: Thread ID "comm" identifier - * (Don't trust this, see below.) - * @KDBUS_ITEM_PID_COMM: Process ID "comm" identifier - * (Don't trust this, see below.) - * @KDBUS_ITEM_EXE: The path of the executable - * (Don't trust this, see below.) - * @KDBUS_ITEM_CMDLINE: The process command line - * (Don't trust this, see below.) - * @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_DESCRIPTION: The connection's human-readable name - * (debugging) - * @_KDBUS_ITEM_POLICY_BASE: Start of policy items - * @KDBUS_ITEM_POLICY_ACCESS: Policy access block - * @_KDBUS_ITEM_KERNEL_BASE: Start of kernel-generated message items - * @KDBUS_ITEM_NAME_ADD: Notification in kdbus_notify_name_change - * @KDBUS_ITEM_NAME_REMOVE: Notification in kdbus_notify_name_change - * @KDBUS_ITEM_NAME_CHANGE: Notification in kdbus_notify_name_change - * @KDBUS_ITEM_ID_ADD: Notification in kdbus_notify_id_change - * @KDBUS_ITEM_ID_REMOVE: Notification in kdbus_notify_id_change - * @KDBUS_ITEM_REPLY_TIMEOUT: Timeout has been reached - * @KDBUS_ITEM_REPLY_DEAD: Destination died - * - * N.B: The process and thread COMM fields, as well as the CMDLINE and - * EXE fields may be altered by unprivileged processes und should - * hence *not* used for security decisions. Peers should make use of - * these items only for informational purposes, such as generating log - * records. - */ -enum kdbus_item_type { - _KDBUS_ITEM_NULL, - _KDBUS_ITEM_USER_BASE, - KDBUS_ITEM_NEGOTIATE = _KDBUS_ITEM_USER_BASE, - KDBUS_ITEM_PAYLOAD_VEC, - KDBUS_ITEM_PAYLOAD_OFF, - KDBUS_ITEM_PAYLOAD_MEMFD, - KDBUS_ITEM_FDS, - KDBUS_ITEM_CANCEL_FD, - KDBUS_ITEM_BLOOM_PARAMETER, - KDBUS_ITEM_BLOOM_FILTER, - KDBUS_ITEM_BLOOM_MASK, - KDBUS_ITEM_DST_NAME, - KDBUS_ITEM_MAKE_NAME, - KDBUS_ITEM_ATTACH_FLAGS_SEND, - KDBUS_ITEM_ATTACH_FLAGS_RECV, - KDBUS_ITEM_ID, - KDBUS_ITEM_NAME, - KDBUS_ITEM_DST_ID, - - /* keep these item types in sync with KDBUS_ATTACH_* flags */ - _KDBUS_ITEM_ATTACH_BASE = 0x1000, - KDBUS_ITEM_TIMESTAMP = _KDBUS_ITEM_ATTACH_BASE, - KDBUS_ITEM_CREDS, - KDBUS_ITEM_PIDS, - KDBUS_ITEM_AUXGROUPS, - KDBUS_ITEM_OWNED_NAME, - KDBUS_ITEM_TID_COMM, - KDBUS_ITEM_PID_COMM, - KDBUS_ITEM_EXE, - KDBUS_ITEM_CMDLINE, - KDBUS_ITEM_CGROUP, - KDBUS_ITEM_CAPS, - KDBUS_ITEM_SECLABEL, - KDBUS_ITEM_AUDIT, - KDBUS_ITEM_CONN_DESCRIPTION, - - _KDBUS_ITEM_POLICY_BASE = 0x2000, - KDBUS_ITEM_POLICY_ACCESS = _KDBUS_ITEM_POLICY_BASE, - - _KDBUS_ITEM_KERNEL_BASE = 0x8000, - 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 - * @bloom_parameter: KDBUS_ITEM_BLOOM_PARAMETER - * @bloom_filter: KDBUS_ITEM_BLOOM_FILTER - * @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_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_pids pids; - struct kdbus_audit audit; - struct kdbus_caps caps; - struct kdbus_timestamp timestamp; - struct kdbus_name name; - struct kdbus_bloom_parameter bloom_parameter; - struct kdbus_bloom_filter bloom_filter; - struct kdbus_memfd memfd; - int fds[0]; - struct kdbus_notify_name_change name_change; - struct kdbus_notify_id_change id_change; - struct kdbus_policy_access policy_access; - }; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_msg_flags - type of message - * @KDBUS_MSG_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_NO_AUTO_START: Do not start a service if the addressed - * name is not currently active. This flag is - * not looked at by the kernel but only - * serves as hint for userspace implementations. - * @KDBUS_MSG_SIGNAL: Treat this message as signal - */ -enum kdbus_msg_flags { - KDBUS_MSG_EXPECT_REPLY = 1ULL << 0, - KDBUS_MSG_NO_AUTO_START = 1ULL << 1, - KDBUS_MSG_SIGNAL = 1ULL << 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" - * - * Any payload-type is accepted. Common types will get added here once - * established. - */ -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_*), userspace → kernel - * @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, and the send command is - * executed asynchronously, a kernel-generated message - * with an attached KDBUS_ITEM_REPLY_TIMEOUT item - * is sent to @src_id. For synchronously executed send - * command, the value denotes the maximum time the call - * blocks to wait for a reply. The timeout is expected in - * nanoseconds and as absolute CLOCK_MONOTONIC value. - * @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 - * @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; - }; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_msg_info - returned message container - * @offset: Offset of kdbus_msg slice in pool - * @msg_size: Copy of the kdbus_msg.size field - * @return_flags: Command return flags, kernel → userspace - */ -struct kdbus_msg_info { - __u64 offset; - __u64 msg_size; - __u64 return_flags; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_send_flags - flags for sending messages - * @KDBUS_SEND_SYNC_REPLY: Wait for destination connection to - * reply to this message. The - * KDBUS_CMD_SEND ioctl() will block - * until the reply is received, and - * reply in struct kdbus_cmd_send will - * yield the offset in the sender's pool - * where the reply can be found. - * This flag is only valid if - * @KDBUS_MSG_EXPECT_REPLY is set as well. - */ -enum kdbus_send_flags { - KDBUS_SEND_SYNC_REPLY = 1ULL << 0, -}; - -/** - * struct kdbus_cmd_send - send message - * @size: Overall size of this structure - * @flags: Flags to change send behavior (KDBUS_SEND_*) - * @return_flags: Command return flags, kernel → userspace - * @msg_address: Storage address of the kdbus_msg to send - * @reply: Storage for message reply if KDBUS_SEND_SYNC_REPLY - * was given - * @items: Additional items for this command - */ -struct kdbus_cmd_send { - __u64 size; - __u64 flags; - __u64 return_flags; - __u64 msg_address; - struct kdbus_msg_info 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 = 1ULL << 0, - KDBUS_RECV_DROP = 1ULL << 1, - KDBUS_RECV_USE_PRIORITY = 1ULL << 2, -}; - -/** - * enum kdbus_recv_return_flags - return flags for message receive commands - * @KDBUS_RECV_RETURN_INCOMPLETE_FDS: One or more file descriptors could not - * be installed. These descriptors in - * KDBUS_ITEM_FDS will carry the value -1. - * @KDBUS_RECV_RETURN_DROPPED_MSGS: There have been dropped messages since - * the last time a message was received. - * The 'dropped_msgs' counter contains the - * number of messages dropped pool - * overflows or other missed broadcasts. - */ -enum kdbus_recv_return_flags { - KDBUS_RECV_RETURN_INCOMPLETE_FDS = 1ULL << 0, - KDBUS_RECV_RETURN_DROPPED_MSGS = 1ULL << 1, -}; - -/** - * struct kdbus_cmd_recv - struct to de-queue a buffered message - * @size: Overall size of this object - * @flags: KDBUS_RECV_* flags, userspace → kernel - * @return_flags: Command return flags, kernel → userspace - * @priority: Minimum priority of the messages to de-queue. Lowest - * values have the highest priority. - * @dropped_msgs: In case there were any dropped messages since the last - * time a message was received, this will be set to the - * number of lost messages and - * KDBUS_RECV_RETURN_DROPPED_MSGS will be set in - * 'return_flags'. This can only happen if the ioctl - * returns 0 or EAGAIN. - * @msg: Return storage for received message. - * @items: Additional items for this command. - * - * This struct is used with the KDBUS_CMD_RECV ioctl. - */ -struct kdbus_cmd_recv { - __u64 size; - __u64 flags; - __u64 return_flags; - __s64 priority; - __u64 dropped_msgs; - struct kdbus_msg_info msg; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_cmd_free - struct to free a slice of memory in the pool - * @size: Overall size of this structure - * @flags: Flags for the free command, userspace → kernel - * @return_flags: Command return flags, kernel → userspace - * @offset: The offset of the memory slice, as returned by other - * ioctls - * @items: Additional items to modify the behavior - * - * This struct is used with the KDBUS_CMD_FREE ioctl. - */ -struct kdbus_cmd_free { - __u64 size; - __u64 flags; - __u64 return_flags; - __u64 offset; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_hello_flags - flags for struct kdbus_cmd_hello - * @KDBUS_HELLO_ACCEPT_FD: The connection allows the reception 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_POLICY_HOLDER: Special-purpose connection which registers - * policy entries for a name. The provided name - * is not activated and not registered with the - * name database, it only allows unprivileged - * connections to acquire a name, talk or discover - * a service - * @KDBUS_HELLO_MONITOR: Special-purpose connection to monitor - * bus traffic - */ -enum kdbus_hello_flags { - KDBUS_HELLO_ACCEPT_FD = 1ULL << 0, - KDBUS_HELLO_ACTIVATOR = 1ULL << 1, - KDBUS_HELLO_POLICY_HOLDER = 1ULL << 2, - KDBUS_HELLO_MONITOR = 1ULL << 3, -}; - -/** - * struct kdbus_cmd_hello - struct to say hello to kdbus - * @size: The total size of the structure - * @flags: Connection flags (KDBUS_HELLO_*), userspace → kernel - * @return_flags: Command return flags, kernel → userspace - * @attach_flags_send: Mask of metadata to attach to each message sent - * off by this connection (KDBUS_ATTACH_*) - * @attach_flags_recv: Mask of metadata to attach to each message receieved - * by the new connection (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) - * @pool_size: Size of the connection's buffer where the received - * messages are placed - * @offset: Pool offset where items are returned to report - * additional information about the bus and the newly - * created connection. - * @items_size: Size of buffer returned in the pool slice at @offset. - * @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 flags; - __u64 return_flags; - __u64 attach_flags_send; - __u64 attach_flags_recv; - __u64 bus_flags; - __u64 id; - __u64 pool_size; - __u64 offset; - __u64 items_size; - __u8 id128[16]; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_info - connection information - * @size: total size of the struct - * @id: 64bit object ID - * @flags: object creation flags - * @items: list of items - * - * Note that the user is responsible for freeing the allocated memory with - * the KDBUS_CMD_FREE ioctl. - */ -struct kdbus_info { - __u64 size; - __u64 id; - __u64 flags; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_list_flags - what to include into the returned list - * @KDBUS_LIST_UNIQUE: active connections - * @KDBUS_LIST_ACTIVATORS: activator connections - * @KDBUS_LIST_NAMES: known well-known names - * @KDBUS_LIST_QUEUED: queued-up names - */ -enum kdbus_list_flags { - KDBUS_LIST_UNIQUE = 1ULL << 0, - KDBUS_LIST_NAMES = 1ULL << 1, - KDBUS_LIST_ACTIVATORS = 1ULL << 2, - KDBUS_LIST_QUEUED = 1ULL << 3, -}; - -/** - * struct kdbus_cmd_list - list connections - * @size: overall size of this object - * @flags: flags for the query (KDBUS_LIST_*), userspace → kernel - * @return_flags: command return flags, kernel → userspace - * @offset: Offset in the caller's pool buffer where an array of - * kdbus_info objects is stored. - * The user must use KDBUS_CMD_FREE to free the - * allocated memory. - * @list_size: size of returned list in bytes - * @items: Items for the command. Reserved for future use. - * - * This structure is used with the KDBUS_CMD_LIST ioctl. - */ -struct kdbus_cmd_list { - __u64 size; - __u64 flags; - __u64 return_flags; - __u64 offset; - __u64 list_size; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_cmd_info - struct used for KDBUS_CMD_CONN_INFO ioctl - * @size: The total size of the struct - * @flags: Flags for this ioctl, userspace → kernel - * @return_flags: Command return flags, kernel → userspace - * @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. - * @attach_flags: Set of attach flags to specify the set of information - * to receive, userspace → kernel - * @offset: Returned offset in the caller's pool buffer where the - * kdbus_info struct result is stored. The user must - * use KDBUS_CMD_FREE to free the allocated memory. - * @info_size: Output buffer to report size of data at @offset. - * @items: The optional item list, containing the - * well-known name to look up as a KDBUS_ITEM_NAME. - * 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_info. - */ -struct kdbus_cmd_info { - __u64 size; - __u64 flags; - __u64 return_flags; - __u64 id; - __u64 attach_flags; - __u64 offset; - __u64 info_size; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_cmd_match_flags - flags to control the KDBUS_CMD_MATCH_ADD ioctl - * @KDBUS_MATCH_REPLACE: If entries with the supplied cookie already - * exists, remove them before installing the new - * matches. - */ -enum kdbus_cmd_match_flags { - KDBUS_MATCH_REPLACE = 1ULL << 0, -}; - -/** - * struct kdbus_cmd_match - struct to add or remove matches - * @size: The total size of the struct - * @flags: Flags for match command (KDBUS_MATCH_*), - * userspace → kernel - * @return_flags: Command return flags, kernel → userspace - * @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_MATCH_ADD and - * KDBUS_CMD_MATCH_REMOVE ioctl. - */ -struct kdbus_cmd_match { - __u64 size; - __u64 flags; - __u64 return_flags; - __u64 cookie; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_make_flags - Flags for KDBUS_CMD_{BUS,ENDPOINT}_MAKE - * @KDBUS_MAKE_ACCESS_GROUP: Make the bus or endpoint node group-accessible - * @KDBUS_MAKE_ACCESS_WORLD: Make the bus or endpoint node world-accessible - */ -enum kdbus_make_flags { - KDBUS_MAKE_ACCESS_GROUP = 1ULL << 0, - KDBUS_MAKE_ACCESS_WORLD = 1ULL << 1, -}; - -/** - * enum kdbus_name_flags - flags for KDBUS_CMD_NAME_ACQUIRE - * @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 = 1ULL << 0, - KDBUS_NAME_ALLOW_REPLACEMENT = 1ULL << 1, - KDBUS_NAME_QUEUE = 1ULL << 2, - KDBUS_NAME_IN_QUEUE = 1ULL << 3, - KDBUS_NAME_ACTIVATOR = 1ULL << 4, -}; - -/** - * struct kdbus_cmd - generic ioctl payload - * @size: Overall size of this structure - * @flags: Flags for this ioctl, userspace → kernel - * @return_flags: Ioctl return flags, kernel → userspace - * @items: Additional items to modify the behavior - * - * This is a generic ioctl payload object. It's used by all ioctls that only - * take flags and items as input. - */ -struct kdbus_cmd { - __u64 size; - __u64 flags; - __u64 return_flags; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * Ioctl API - * - * KDBUS_CMD_BUS_MAKE: After opening the "control" node, this command - * creates a new bus with the specified - * name. The bus is immediately shut down and - * cleaned up when the opened file descriptor is - * closed. - * - * KDBUS_CMD_ENDPOINT_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_ENDPOINT_UPDATE: Update the properties of a custom enpoint. Used - * to update the policy. - * - * KDBUS_CMD_HELLO: By opening the bus node, a connection is - * created. After a HELLO the opened connection - * becomes an active peer on the bus. - * KDBUS_CMD_UPDATE: Update the properties of a connection. Used to - * update the metadata subscription mask and - * policy. - * KDBUS_CMD_BYEBYE: Disconnect a connection. If there are no - * messages queued up in the connection's pool, - * the call succeeds, and the handle is rendered - * unusable. Otherwise, -EBUSY is returned without - * any further side-effects. - * KDBUS_CMD_FREE: Release the allocated memory in the receiver's - * pool. - * 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_BUS_CREATOR_INFO: Retrieve information of the creator of the bus - * a connection is attached to. - * - * KDBUS_CMD_SEND: Send a message and pass data from userspace to - * the kernel. - * KDBUS_CMD_RECV: Receive a message from the kernel which is - * placed 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_LIST: Retrieve the list of all currently registered - * well-known and unique names. - * - * 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. - */ -enum kdbus_ioctl_type { - /* bus owner (00-0f) */ - KDBUS_CMD_BUS_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x00, - struct kdbus_cmd), - - /* endpoint owner (10-1f) */ - KDBUS_CMD_ENDPOINT_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x10, - struct kdbus_cmd), - KDBUS_CMD_ENDPOINT_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x11, - struct kdbus_cmd), - - /* connection owner (80-ff) */ - KDBUS_CMD_HELLO = _IOWR(KDBUS_IOCTL_MAGIC, 0x80, - struct kdbus_cmd_hello), - KDBUS_CMD_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x81, - struct kdbus_cmd), - KDBUS_CMD_BYEBYE = _IOW(KDBUS_IOCTL_MAGIC, 0x82, - struct kdbus_cmd), - KDBUS_CMD_FREE = _IOW(KDBUS_IOCTL_MAGIC, 0x83, - struct kdbus_cmd_free), - KDBUS_CMD_CONN_INFO = _IOR(KDBUS_IOCTL_MAGIC, 0x84, - struct kdbus_cmd_info), - KDBUS_CMD_BUS_CREATOR_INFO = _IOR(KDBUS_IOCTL_MAGIC, 0x85, - struct kdbus_cmd_info), - KDBUS_CMD_LIST = _IOR(KDBUS_IOCTL_MAGIC, 0x86, - struct kdbus_cmd_list), - - KDBUS_CMD_SEND = _IOW(KDBUS_IOCTL_MAGIC, 0x90, - struct kdbus_cmd_send), - KDBUS_CMD_RECV = _IOR(KDBUS_IOCTL_MAGIC, 0x91, - struct kdbus_cmd_recv), - - KDBUS_CMD_NAME_ACQUIRE = _IOW(KDBUS_IOCTL_MAGIC, 0xa0, - struct kdbus_cmd), - KDBUS_CMD_NAME_RELEASE = _IOW(KDBUS_IOCTL_MAGIC, 0xa1, - struct kdbus_cmd), - - KDBUS_CMD_MATCH_ADD = _IOW(KDBUS_IOCTL_MAGIC, 0xb0, - struct kdbus_cmd_match), - KDBUS_CMD_MATCH_REMOVE = _IOW(KDBUS_IOCTL_MAGIC, 0xb1, - struct kdbus_cmd_match), -}; - -#endif /* _UAPI_KDBUS_H_ */ diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c deleted file mode 100644 index ed5f94e136..0000000000 --- a/src/libsystemd/sd-bus/sd-bus.c +++ /dev/null @@ -1,3791 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-container.h" -#include "bus-control.h" -#include "bus-internal.h" -#include "bus-kernel.h" -#include "bus-label.h" -#include "bus-message.h" -#include "bus-objects.h" -#include "bus-protocol.h" -#include "bus-slot.h" -#include "bus-socket.h" -#include "bus-track.h" -#include "bus-type.h" -#include "bus-util.h" -#include "cgroup-util.h" -#include "def.h" -#include "fd-util.h" -#include "hexdecoct.h" -#include "hostname-util.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -#define log_debug_bus_message(m) \ - do { \ - sd_bus_message *_mm = (m); \ - log_debug("Got message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " error=%s", \ - bus_message_type_to_string(_mm->header->type), \ - strna(sd_bus_message_get_sender(_mm)), \ - strna(sd_bus_message_get_destination(_mm)), \ - strna(sd_bus_message_get_path(_mm)), \ - strna(sd_bus_message_get_interface(_mm)), \ - strna(sd_bus_message_get_member(_mm)), \ - BUS_MESSAGE_COOKIE(_mm), \ - _mm->reply_cookie, \ - strna(_mm->error.message)); \ - } while (false) - -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 thread_local sd_bus *default_system_bus = NULL; -static thread_local sd_bus *default_user_bus = NULL; -static thread_local sd_bus *default_starter_bus = NULL; - -static void bus_close_fds(sd_bus *b) { - assert(b); - - detach_io_events(b); - - if (b->input_fd != b->output_fd) - safe_close(b->output_fd); - b->output_fd = b->input_fd = safe_close(b->input_fd); -} - -static void bus_reset_queues(sd_bus *b) { - assert(b); - - while (b->rqueue_size > 0) - sd_bus_message_unref(b->rqueue[--b->rqueue_size]); - - b->rqueue = mfree(b->rqueue); - b->rqueue_allocated = 0; - - while (b->wqueue_size > 0) - sd_bus_message_unref(b->wqueue[--b->wqueue_size]); - - b->wqueue = mfree(b->wqueue); - b->wqueue_allocated = 0; -} - -static void bus_free(sd_bus *b) { - sd_bus_slot *s; - - assert(b); - assert(!b->track_queue); - - b->state = BUS_CLOSED; - - sd_bus_detach_event(b); - - while ((s = b->slots)) { - /* At this point only floating slots can still be - * around, because the non-floating ones keep a - * reference to the bus, and we thus couldn't be - * destructing right now... We forcibly disconnect the - * slots here, so that they still can be referenced by - * apps, but are dead. */ - - assert(s->floating); - bus_slot_disconnect(s); - sd_bus_slot_unref(s); - } - - if (b->default_bus_ptr) - *b->default_bus_ptr = NULL; - - bus_close_fds(b); - - if (b->kdbus_buffer) - munmap(b->kdbus_buffer, KDBUS_POOL_SIZE); - - free(b->label); - 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->description); - - free(b->exec_path); - strv_free(b->exec_argv); - - close_many(b->fds, b->n_fds); - free(b->fds); - - bus_reset_queues(b); - - ordered_hashmap_free_free(b->reply_callbacks); - prioq_free(b->reply_callbacks_prioq); - - assert(b->match_callbacks.type == BUS_MATCH_ROOT); - bus_match_free(&b->match_callbacks); - - hashmap_free_free(b->vtable_methods); - hashmap_free_free(b->vtable_properties); - - assert(hashmap_isempty(b->nodes)); - 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, -EBADF); - assert_return(output_fd >= 0, -EBADF); - 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_set_monitor(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_MONITOR, 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_timestamp(sd_bus *bus, int b) { - uint64_t new_flags; - assert_return(bus, -EINVAL); - assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - new_flags = bus->attach_flags; - SET_FLAG(new_flags, KDBUS_ATTACH_TIMESTAMP, b); - - if (bus->attach_flags == new_flags) - return 0; - - bus->attach_flags = new_flags; - if (bus->state != BUS_UNSET && bus->is_kernel) - bus_kernel_realize_attach_flags(bus); - - return 0; -} - -_public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) { - uint64_t new_flags; - - assert_return(bus, -EINVAL); - assert_return(mask <= _SD_BUS_CREDS_ALL, -EINVAL); - assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - SET_FLAG(bus->creds_mask, mask, b); - - /* The well knowns we need unconditionally, so that matches can work */ - bus->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME; - - /* Make sure we don't lose the timestamp flag */ - new_flags = (bus->attach_flags & KDBUS_ATTACH_TIMESTAMP) | attach_flags_to_kdbus(bus->creds_mask); - if (bus->attach_flags == new_flags) - return 0; - - bus->attach_flags = new_flags; - if (bus->state != BUS_UNSET && bus->is_kernel) - bus_kernel_realize_attach_flags(bus); - - return 0; -} - -_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; -} - -_public_ int sd_bus_set_description(sd_bus *bus, const char *description) { - assert_return(bus, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return free_and_strdup(&bus->description, description); -} - -_public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - bus->allow_interactive_authorization = !!b; - return 0; -} - -_public_ int sd_bus_get_allow_interactive_authorization(sd_bus *bus) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return bus->allow_interactive_authorization; -} - -static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { - const char *s; - sd_bus *bus; - int r; - - assert(reply); - bus = reply->bus; - assert(bus); - assert(bus->state == BUS_HELLO || bus->state == BUS_CLOSING); - - r = sd_bus_message_get_errno(reply); - 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_(sd_bus_message_unrefp) 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, - &m, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "Hello"); - if (r < 0) - return r; - - return sd_bus_call_async(bus, NULL, m, hello_callback, NULL, 0); -} - -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, *pid = 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; - - r = parse_address_key(p, "pid", &pid); - if (r < 0) - return r; - else if (r > 0) - continue; - - skip_address_key(p); - } - - if (!machine == !pid) - return -EINVAL; - - if (machine) { - if (!machine_name_is_valid(machine)) - return -EINVAL; - - free(b->machine); - b->machine = machine; - machine = NULL; - } else { - b->machine = mfree(b->machine); - } - - if (pid) { - r = parse_pid(pid, &b->nspid); - if (r < 0) - return r; - } else - b->nspid = 0; - - 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 = SOCKADDR_UN_LEN(b->sockaddr.un); - - return 0; -} - -static int parse_container_kernel_address(sd_bus *b, const char **p, char **guid) { - _cleanup_free_ char *machine = NULL, *pid = 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; - - r = parse_address_key(p, "pid", &pid); - if (r < 0) - return r; - else if (r > 0) - continue; - - skip_address_key(p); - } - - if (!machine == !pid) - return -EINVAL; - - if (machine) { - if (!machine_name_is_valid(machine)) - return -EINVAL; - - free(b->machine); - b->machine = machine; - machine = NULL; - } else { - b->machine = mfree(b->machine); - } - - if (pid) { - r = parse_pid(pid, &b->nspid); - if (r < 0) - return r; - } else - b->nspid = 0; - - r = free_and_strdup(&b->kernel, "/sys/fs/kdbus/0-system/bus"); - if (r < 0) - return r; - - return 0; -} - -static void bus_reset_parsed_address(sd_bus *b) { - assert(b); - - zero(b->sockaddr); - b->sockaddr_size = 0; - b->exec_argv = strv_free(b->exec_argv); - b->exec_path = mfree(b->exec_path); - b->server_id = SD_ID128_NULL; - b->kernel = mfree(b->kernel); - b->machine = mfree(b->machine); - b->nspid = 0; -} - -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-machine-unix:")) { - - a += 15; - r = parse_container_unix_address(b, &a, &guid); - if (r < 0) - return r; - - break; - } else if (startswith(a, "x-machine-kernel:")) { - - a += 17; - 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) { - bool container_kdbus_available = false; - bool kdbus_available = false; - int r; - - assert(b); - - for (;;) { - bool skipped = false; - - bus_close_fds(b); - - /* - * Usually, if you provide multiple different bus-addresses, we - * try all of them in order. We use the first one that - * succeeds. However, if you mix kernel and unix addresses, we - * never try unix-addresses if a previous kernel address was - * tried and kdbus was available. This is required to prevent - * clients to fallback to the bus-proxy if kdbus is available - * but failed (eg., too many connections). - */ - - if (b->exec_path) - r = bus_socket_exec(b); - else if ((b->nspid > 0 || b->machine) && b->kernel) { - r = bus_container_connect_kernel(b); - if (r < 0 && !IN_SET(r, -ENOENT, -ESOCKTNOSUPPORT)) - container_kdbus_available = true; - - } else if ((b->nspid > 0 || b->machine) && b->sockaddr.sa.sa_family != AF_UNSPEC) { - if (!container_kdbus_available) - r = bus_container_connect_socket(b); - else - skipped = true; - - } else if (b->kernel) { - r = bus_kernel_connect(b); - if (r < 0 && !IN_SET(r, -ENOENT, -ESOCKTNOSUPPORT)) - kdbus_available = true; - - } else if (b->sockaddr.sa.sa_family != AF_UNSPEC) { - if (!kdbus_available) - r = bus_socket_connect(b); - else - skipped = true; - } 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) { - sd_bus_close(bus); - return r; - } - - return bus_send_hello(bus); -} - -_public_ int sd_bus_open(sd_bus **ret) { - const char *e; - sd_bus *b; - int r; - - assert_return(ret, -EINVAL); - - /* Let's connect to the starter bus if it is set, and - * otherwise to the bus that is appropropriate for the scope - * we are running in */ - - e = secure_getenv("DBUS_STARTER_BUS_TYPE"); - if (e) { - if (streq(e, "system")) - return sd_bus_open_system(ret); - else if (STR_IN_SET(e, "session", "user")) - return sd_bus_open_user(ret); - } - - e = secure_getenv("DBUS_STARTER_ADDRESS"); - if (!e) { - if (cg_pid_get_owner_uid(0, NULL) >= 0) - return sd_bus_open_user(ret); - else - return sd_bus_open_system(ret); - } - - r = sd_bus_new(&b); - if (r < 0) - return r; - - r = sd_bus_set_address(b, e); - if (r < 0) - goto fail; - - b->bus_client = true; - - /* We don't know whether the bus is trusted or not, so better - * be safe, and authenticate everything */ - b->trusted = false; - b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; - b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; - - r = sd_bus_start(b); - if (r < 0) - goto fail; - - *ret = b; - return 0; - -fail: - bus_free(b); - return r; -} - -int bus_set_address_system(sd_bus *b) { - const char *e; - assert(b); - - e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS"); - if (e) - return sd_bus_set_address(b, e); - - return sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_ADDRESS); -} - -_public_ int sd_bus_open_system(sd_bus **ret) { - sd_bus *b; - int r; - - assert_return(ret, -EINVAL); - - r = sd_bus_new(&b); - if (r < 0) - return r; - - r = bus_set_address_system(b); - if (r < 0) - goto fail; - - b->bus_client = true; - b->is_system = 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; - b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; - - r = sd_bus_start(b); - if (r < 0) - goto fail; - - *ret = b; - return 0; - -fail: - bus_free(b); - return r; -} - -int bus_set_address_user(sd_bus *b) { - const char *e; - uid_t uid; - int r; - - assert(b); - - e = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); - if (e) - return sd_bus_set_address(b, e); - - r = cg_pid_get_owner_uid(0, &uid); - if (r < 0) - uid = getuid(); - - e = secure_getenv("XDG_RUNTIME_DIR"); - if (e) { - _cleanup_free_ char *ee = NULL; - - ee = bus_address_escape(e); - if (!ee) - return -ENOMEM; - - (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, ee); - } else - (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT, uid); - - if (!b->address) - return -ENOMEM; - - return 0; -} - -_public_ int sd_bus_open_user(sd_bus **ret) { - sd_bus *b; - int r; - - assert_return(ret, -EINVAL); - - r = sd_bus_new(&b); - if (r < 0) - return r; - - r = bus_set_address_user(b); - if (r < 0) - return r; - - b->bus_client = true; - b->is_user = 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; -} - -int bus_set_address_system_remote(sd_bus *b, const char *host) { - _cleanup_free_ char *e = NULL; - char *m = NULL, *c = NULL; - - assert(b); - assert(host); - - /* Let's see if we shall enter some container */ - m = strchr(host, ':'); - if (m) { - m++; - - /* Let's make sure this is not a port of some kind, - * and is a valid machine name. */ - if (!in_charset(m, "0123456789") && machine_name_is_valid(m)) { - char *t; - - /* Cut out the host part */ - t = strndupa(host, m - host - 1); - e = bus_address_escape(t); - if (!e) - return -ENOMEM; - - c = strjoina(",argv4=--machine=", m); - } - } - - if (!e) { - e = bus_address_escape(host); - if (!e) - return -ENOMEM; - } - - b->address = strjoin("unixexec:path=ssh,argv1=-xT,argv2=", e, ",argv3=systemd-stdio-bridge", c, NULL); - if (!b->address) - return -ENOMEM; - - return 0; - } - -_public_ int sd_bus_open_system_remote(sd_bus **ret, const char *host) { - sd_bus *bus; - int r; - - assert_return(host, -EINVAL); - assert_return(ret, -EINVAL); - - r = sd_bus_new(&bus); - if (r < 0) - return r; - - r = bus_set_address_system_remote(bus, host); - if (r < 0) - goto fail; - - bus->bus_client = true; - bus->trusted = false; - bus->is_system = true; - - r = sd_bus_start(bus); - if (r < 0) - goto fail; - - *ret = bus; - return 0; - -fail: - bus_free(bus); - return r; -} - -int bus_set_address_system_machine(sd_bus *b, const char *machine) { - _cleanup_free_ char *e = NULL; - - assert(b); - assert(machine); - - e = bus_address_escape(machine); - if (!e) - return -ENOMEM; - - b->address = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL); - if (!b->address) - return -ENOMEM; - - return 0; -} - -_public_ int sd_bus_open_system_machine(sd_bus **ret, const char *machine) { - sd_bus *bus; - int r; - - assert_return(machine, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(machine_name_is_valid(machine), -EINVAL); - - r = sd_bus_new(&bus); - if (r < 0) - return r; - - r = bus_set_address_system_machine(bus, machine); - if (r < 0) - goto fail; - - bus->bus_client = true; - bus->trusted = false; - bus->is_system = true; - - r = sd_bus_start(bus); - if (r < 0) - goto fail; - - *ret = bus; - return 0; - -fail: - bus_free(bus); - return r; -} - -_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. */ -} - -_public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) { - - if (!bus) - return NULL; - - sd_bus_flush(bus); - sd_bus_close(bus); - - return sd_bus_unref(bus); -} - -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) { - - if (!bus) - return 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 > 0) - return NULL; - - 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 (bus->hello_flags & KDBUS_HELLO_MONITOR) - return 0; - - 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_bus_id(sd_bus *bus, sd_id128_t *id) { - int r; - - assert_return(bus, -EINVAL); - assert_return(id, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - r = bus_ensure_running(bus); - if (r < 0) - return r; - - *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) { - bool remarshal = false; - - assert(b); - - /* wrong packet version */ - if (b->message_version != 0 && b->message_version != (*m)->header->version) - remarshal = true; - - /* wrong packet endianness */ - if (b->message_endian != 0 && b->message_endian != (*m)->header->endian) - remarshal = true; - - /* TODO: kdbus-messages received from the kernel contain data which is - * not allowed to be passed to KDBUS_CMD_SEND. Therefore, we have to - * force remarshaling of the message. Technically, we could just - * recreate the kdbus message, but that is non-trivial as other parts of - * the message refer to m->kdbus already. This should be fixed! */ - if ((*m)->kdbus && (*m)->release_kdbus) - remarshal = true; - - return remarshal ? bus_message_remarshal(b, m) : 0; -} - -int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) { - assert(b); - assert(m); - - /* Fake some timestamps, if they were requested, and not - * already initialized */ - if (b->attach_flags & KDBUS_ATTACH_TIMESTAMP) { - if (m->realtime <= 0) - m->realtime = now(CLOCK_REALTIME); - - if (m->monotonic <= 0) - m->monotonic = now(CLOCK_MONOTONIC); - } - - /* 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, artificial 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, bool hint_sync_call, size_t *idx) { - int r; - - assert(bus); - assert(m); - - if (bus->is_kernel) - r = bus_kernel_write_message(bus, m, hint_sync_call); - 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=%" PRIu64 " reply_cookie=%" PRIu64 " 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)), - BUS_MESSAGE_COOKIE(m), - 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], false, &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, bool hint_priority, int64_t priority) { - assert(bus); - - if (bus->is_kernel) - return bus_kernel_read_message(bus, hint_priority, priority); - 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, bool hint_priority, int64_t priority, sd_bus_message **m) { - int r, ret = 0; - - assert(bus); - assert(m); - assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); - - /* Note that the priority logic is only available on kdbus, - * where the rqueue is unused. We check the rqueue here - * anyway, because it's simple... */ - - 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, hint_priority, priority); - if (r < 0) - return r; - if (r == 0) - return ret; - - ret = 1; - } -} - -static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, bool hint_sync_call) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); - int r; - - assert_return(m, -EINVAL); - - if (!bus) - bus = m->bus; - - assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - 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 -EOPNOTSUPP; - } - - /* 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) - goto finish; - - if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) { - size_t idx = 0; - - r = bus_write_message(bus, m, hint_sync_call, &idx); - if (r < 0) { - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - return -ECONNRESET; - } - - return r; - } - - 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); - } - -finish: - if (cookie) - *cookie = BUS_MESSAGE_COOKIE(m); - - return 1; -} - -_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) { - return bus_send_internal(bus, m, cookie, false); -} - -_public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie) { - int r; - - assert_return(m, -EINVAL); - - if (!bus) - bus = m->bus; - - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - 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_slot **slot, - sd_bus_message *_m, - sd_bus_message_handler_t callback, - void *userdata, - uint64_t usec) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); - _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *s = NULL; - int r; - - 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); - - if (!bus) - bus = m->bus; - - assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - r = ordered_hashmap_ensure_allocated(&bus->reply_callbacks, &uint64_hash_ops); - 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; - - s = bus_slot_allocate(bus, !slot, BUS_REPLY_CALLBACK, sizeof(struct reply_callback), userdata); - if (!s) - return -ENOMEM; - - s->reply_callback.callback = callback; - - s->reply_callback.cookie = BUS_MESSAGE_COOKIE(m); - r = ordered_hashmap_put(bus->reply_callbacks, &s->reply_callback.cookie, &s->reply_callback); - if (r < 0) { - s->reply_callback.cookie = 0; - return r; - } - - s->reply_callback.timeout = calc_elapse(m->timeout); - if (s->reply_callback.timeout != 0) { - r = prioq_put(bus->reply_callbacks_prioq, &s->reply_callback, &s->reply_callback.prioq_idx); - if (r < 0) { - s->reply_callback.timeout = 0; - return r; - } - } - - r = sd_bus_send(bus, m, &s->reply_callback.cookie); - if (r < 0) - return r; - - if (slot) - *slot = s; - s = NULL; - - return r; -} - -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_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); - usec_t timeout; - uint64_t cookie; - unsigned i; - int r; - - bus_assert_return(m, -EINVAL, error); - bus_assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL, error); - bus_assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL, error); - bus_assert_return(!bus_error_is_dirty(error), -EINVAL, error); - - if (!bus) - bus = m->bus; - - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - bus_assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - r = bus_ensure_running(bus); - if (r < 0) - goto fail; - - i = bus->rqueue_size; - - r = bus_seal_message(bus, m, usec); - if (r < 0) - goto fail; - - r = bus_remarshal_message(bus, &m); - if (r < 0) - goto fail; - - r = bus_send_internal(bus, m, &cookie, true); - if (r < 0) - goto fail; - - 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--; - log_debug_bus_message(incoming); - - if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) { - - if (incoming->n_fds <= 0 || (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) { - if (reply) - *reply = incoming; - else - sd_bus_message_unref(incoming); - - return 1; - } - - r = sd_bus_error_setf(error, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptors which I couldn't accept. Sorry."); - sd_bus_message_unref(incoming); - return r; - - } else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR) { - r = sd_bus_error_copy(error, &incoming->error); - sd_bus_message_unref(incoming); - return r; - } else { - r = -EIO; - goto fail; - } - - } 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); - r = -ELOOP; - goto fail; - } - - /* Try to read more, right-away */ - i++; - } - - r = bus_read_message(bus, false, 0); - if (r < 0) { - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = -ECONNRESET; - } - - goto fail; - } - if (r > 0) - continue; - - if (timeout > 0) { - usec_t n; - - n = now(CLOCK_MONOTONIC); - if (n >= timeout) { - r = -ETIMEDOUT; - goto fail; - } - - left = timeout - n; - } else - left = (uint64_t) -1; - - r = bus_poll(bus, true, left); - if (r < 0) - goto fail; - if (r == 0) { - r = -ETIMEDOUT; - goto fail; - } - - r = dispatch_wqueue(bus); - if (r < 0) { - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = -ECONNRESET; - } - - goto fail; - } - } - -fail: - return sd_bus_error_set_errno(error, 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_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING) - return -ENOTCONN; - - 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_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING) - return -ENOTCONN; - - if (bus->track_queue) { - *timeout_usec = 0; - return 1; - } - - 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; - } - - if (c->timeout == 0) { - *timeout_usec = (uint64_t) -1; - return 0; - } - - *timeout_usec = c->timeout; - return 1; -} - -static int process_timeout(sd_bus *bus) { - _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message* m = NULL; - struct reply_callback *c; - sd_bus_slot *slot; - 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; - - r = bus_seal_synthetic_message(bus, m); - if (r < 0) - return r; - - assert_se(prioq_pop(bus->reply_callbacks_prioq) == c); - c->timeout = 0; - - ordered_hashmap_remove(bus->reply_callbacks, &c->cookie); - c->cookie = 0; - - slot = container_of(c, sd_bus_slot, reply_callback); - - bus->iteration_counter++; - - bus->current_message = m; - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = c->callback; - bus->current_userdata = slot->userdata; - r = c->callback(m, slot->userdata, &error_buffer); - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = NULL; - bus->current_message = NULL; - - if (slot->floating) { - bus_slot_disconnect(slot); - sd_bus_slot_unref(slot); - } - - sd_bus_slot_unref(slot); - - return bus_maybe_reply_error(m, r, &error_buffer); -} - -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 != 1) - return -EIO; - - return 0; -} - -static int process_reply(sd_bus *bus, sd_bus_message *m) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *synthetic_reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; - struct reply_callback *c; - sd_bus_slot *slot; - 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; - - if (bus->is_kernel && (bus->hello_flags & KDBUS_HELLO_MONITOR)) - return 0; - - if (m->destination && bus->unique_name && !streq_ptr(m->destination, bus->unique_name)) - return 0; - - c = ordered_hashmap_remove(bus->reply_callbacks, &m->reply_cookie); - if (!c) - return 0; - - c->cookie = 0; - - slot = container_of(c, sd_bus_slot, reply_callback); - - if (m->n_fds > 0 && !(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) { - - /* If the reply contained a file descriptor which we - * didn't want we pass an error instead. */ - - r = bus_message_new_synthetic_error( - bus, - m->reply_cookie, - &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptor"), - &synthetic_reply); - if (r < 0) - return r; - - /* Copy over original timestamp */ - synthetic_reply->realtime = m->realtime; - synthetic_reply->monotonic = m->monotonic; - synthetic_reply->seqnum = m->seqnum; - - r = bus_seal_synthetic_message(bus, synthetic_reply); - if (r < 0) - return r; - - m = synthetic_reply; - } else { - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - } - - if (c->timeout != 0) { - prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); - c->timeout = 0; - } - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = c->callback; - bus->current_userdata = slot->userdata; - r = c->callback(m, slot->userdata, &error_buffer); - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = NULL; - - if (slot->floating) { - bus_slot_disconnect(slot); - sd_bus_slot_unref(slot); - } - - sd_bus_slot_unref(slot); - - return bus_maybe_reply_error(m, r, &error_buffer); -} - -static int process_filter(sd_bus *bus, sd_bus_message *m) { - _cleanup_(sd_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) { - sd_bus_slot *slot; - - 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; - - slot = container_of(l, sd_bus_slot, filter_callback); - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = l->callback; - bus->current_userdata = slot->userdata; - r = l->callback(m, slot->userdata, &error_buffer); - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - - 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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - assert(bus); - assert(m); - - if (bus->hello_flags & KDBUS_HELLO_MONITOR) - return 0; - - 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_fd_check(sd_bus *bus, sd_bus_message *m) { - assert(bus); - assert(m); - - /* If we got a message with a file descriptor which we didn't - * want to accept, then let's drop it. How can this even - * happen? For example, when the kernel queues a message into - * an activatable names's queue which allows fds, and then is - * delivered to us later even though we ourselves did not - * negotiate it. */ - - if (bus->hello_flags & KDBUS_HELLO_MONITOR) - return 0; - - if (m->n_fds <= 0) - return 0; - - if (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD) - return 0; - - if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) - return 1; /* just eat it up */ - - return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Message contains file descriptors, which I cannot accept. Sorry."); -} - -static int process_message(sd_bus *bus, sd_bus_message *m) { - int r; - - assert(bus); - assert(m); - - bus->current_message = m; - bus->iteration_counter++; - - log_debug_bus_message(m); - - r = process_hello(bus, m); - if (r != 0) - goto finish; - - r = process_reply(bus, m); - if (r != 0) - goto finish; - - r = process_fd_check(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_message = NULL; - return r; -} - -static int dispatch_track(sd_bus *bus) { - assert(bus); - - if (!bus->track_queue) - return 0; - - bus_track_dispatch(bus->track_queue); - return 1; -} - -static int process_running(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) { - _cleanup_(sd_bus_message_unrefp) 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_track(bus); - if (r != 0) - goto null_message; - - r = dispatch_rqueue(bus, hint_priority, priority, &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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - struct reply_callback *c; - int r; - - assert(bus); - assert(bus->state == BUS_CLOSING); - - c = ordered_hashmap_first(bus->reply_callbacks); - if (c) { - _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; - sd_bus_slot *slot; - - /* 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); - c->timeout = 0; - } - - ordered_hashmap_remove(bus->reply_callbacks, &c->cookie); - c->cookie = 0; - - slot = container_of(c, sd_bus_slot, reply_callback); - - bus->iteration_counter++; - - bus->current_message = m; - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = c->callback; - bus->current_userdata = slot->userdata; - r = c->callback(m, slot->userdata, &error_buffer); - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = NULL; - bus->current_message = NULL; - - if (slot->floating) { - bus_slot_disconnect(slot); - sd_bus_slot_unref(slot); - } - - sd_bus_slot_unref(slot); - - return bus_maybe_reply_error(m, r, &error_buffer); - } - - /* Then, synthesize a Disconnected message */ - r = sd_bus_message_new_signal( - bus, - &m, - "/org/freedesktop/DBus/Local", - "org.freedesktop.DBus.Local", - "Disconnected"); - if (r < 0) - return r; - - bus_message_set_sender_local(bus, m); - - r = bus_seal_synthetic_message(bus, m); - if (r < 0) - return r; - - sd_bus_close(bus); - - bus->current_message = 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_message = NULL; - - return r; -} - -static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priority, 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_message, -EBUSY); - assert(!bus->current_slot); - - switch (bus->state) { - - case BUS_UNSET: - return -ENOTCONN; - - case BUS_CLOSED: - return -ECONNRESET; - - case BUS_OPENING: - r = bus_socket_process_opening(bus); - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -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 (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -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, hint_priority, priority, ret); - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -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"); -} - -_public_ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { - return bus_process_internal(bus, false, 0, ret); -} - -_public_ int sd_bus_process_priority(sd_bus *bus, int64_t priority, sd_bus_message **ret) { - return bus_process_internal(bus, true, priority, ret); -} - -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_INFINITY; - - assert(bus); - - if (bus->state == BUS_CLOSING) - return 1; - - if (!BUS_IS_OPEN(bus->state)) - return -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; - - if (!BUS_IS_OPEN(bus->state)) - return -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; - - if (!BUS_IS_OPEN(bus->state)) - return -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 (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -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_slot **slot, - sd_bus_message_handler_t callback, - void *userdata) { - - sd_bus_slot *s; - - assert_return(bus, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - s = bus_slot_allocate(bus, !slot, BUS_FILTER_CALLBACK, sizeof(struct filter_callback), userdata); - if (!s) - return -ENOMEM; - - s->filter_callback.callback = callback; - - bus->filter_callbacks_modified = true; - LIST_PREPEND(callbacks, bus->filter_callbacks, &s->filter_callback); - - if (slot) - *slot = s; - - return 0; -} - -_public_ int sd_bus_add_match( - sd_bus *bus, - sd_bus_slot **slot, - const char *match, - sd_bus_message_handler_t callback, - void *userdata) { - - struct bus_match_component *components = NULL; - unsigned n_components = 0; - sd_bus_slot *s = NULL; - 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; - - s = bus_slot_allocate(bus, !slot, BUS_MATCH_CALLBACK, sizeof(struct match_callback), userdata); - if (!s) { - r = -ENOMEM; - goto finish; - } - - s->match_callback.callback = callback; - s->match_callback.cookie = ++bus->match_cookie; - - if (bus->bus_client) { - enum bus_match_scope scope; - - scope = bus_match_get_scope(components, n_components); - - /* Do not install server-side matches for matches - * against the local service, interface or bus - * path. */ - if (scope != BUS_MATCH_LOCAL) { - - if (!bus->is_kernel) { - /* When this is not a kernel transport, we - * store the original match string, so that we - * can use it to remove the match again */ - - s->match_callback.match_string = strdup(match); - if (!s->match_callback.match_string) { - r = -ENOMEM; - goto finish; - } - } - - r = bus_add_match_internal(bus, s->match_callback.match_string, components, n_components, s->match_callback.cookie); - if (r < 0) - goto finish; - - s->match_added = true; - } - } - - bus->match_callbacks_modified = true; - r = bus_match_add(&bus->match_callbacks, components, n_components, &s->match_callback); - if (r < 0) - goto finish; - - if (slot) - *slot = s; - s = NULL; - -finish: - bus_match_parse_free(components, n_components); - sd_bus_slot_unref(s); - - return r; -} - -int bus_remove_match_by_string( - sd_bus *bus, - const char *match, - sd_bus_message_handler_t callback, - void *userdata) { - - struct bus_match_component *components = NULL; - unsigned n_components = 0; - struct match_callback *c; - 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; - - r = bus_match_find(&bus->match_callbacks, components, n_components, NULL, NULL, &c); - if (r <= 0) - goto finish; - - sd_bus_slot_unref(container_of(c, sd_bus_slot, match_callback)); - -finish: - bus_match_parse_free(components, n_components); - - return r; -} - -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); - sd_bus_close(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_io_event_source, bus->input_fd, 0, io_callback, bus); - 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); - if (r < 0) - return r; - - r = sd_event_source_set_description(bus->input_io_event_source, "bus-input"); - } 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_io_event_source, bus->output_fd, 0, io_callback, bus); - if (r < 0) - return r; - - r = sd_event_source_set_priority(bus->output_io_event_source, bus->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(bus->input_io_event_source, "bus-output"); - } 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_time(bus->event, &bus->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, bus); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(bus->time_event_source, priority); - if (r < 0) - goto fail; - - r = sd_event_source_set_description(bus->time_event_source, "bus-time"); - if (r < 0) - goto fail; - - r = sd_event_add_exit(bus->event, &bus->quit_event_source, quit_callback, bus); - if (r < 0) - goto fail; - - r = sd_event_source_set_description(bus->quit_event_source, "bus-exit"); - 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); - } - - 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_message(sd_bus *bus) { - assert_return(bus, NULL); - - return bus->current_message; -} - -_public_ sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus) { - assert_return(bus, NULL); - - return bus->current_slot; -} - -_public_ sd_bus_message_handler_t sd_bus_get_current_handler(sd_bus *bus) { - assert_return(bus, NULL); - - return bus->current_handler; -} - -_public_ void* sd_bus_get_current_userdata(sd_bus *bus) { - assert_return(bus, NULL); - - return bus->current_userdata; -} - -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) { - return bus_default(sd_bus_open_system, &default_system_bus, ret); -} - - -_public_ int sd_bus_default_user(sd_bus **ret) { - return bus_default(sd_bus_open_user, &default_user_bus, ret); -} - -_public_ int sd_bus_default(sd_bus **ret) { - - const char *e; - - /* Let's try our best to reuse another cached connection. If - * the starter bus type is set, connect via our normal - * connection logic, ignoring $DBUS_STARTER_ADDRESS, so that - * we can share the connection with the user/system default - * bus. */ - - e = secure_getenv("DBUS_STARTER_BUS_TYPE"); - if (e) { - if (streq(e, "system")) - return sd_bus_default_system(ret); - else if (STR_IN_SET(e, "user", "session")) - return sd_bus_default_user(ret); - } - - /* No type is specified, so we have not other option than to - * use the starter address if it is set. */ - - e = secure_getenv("DBUS_STARTER_ADDRESS"); - if (e) { - - return bus_default(sd_bus_open, &default_starter_bus, ret); - } - - /* Finally, if nothing is set use the cached connection for - * the right scope */ - - if (cg_pid_get_owner_uid(0, NULL) >= 0) - return sd_bus_default_user(ret); - else - return sd_bus_default_system(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_ int sd_bus_path_encode(const char *prefix, const char *external_id, char **ret_path) { - _cleanup_free_ char *e = NULL; - char *ret; - - assert_return(object_path_is_valid(prefix), -EINVAL); - assert_return(external_id, -EINVAL); - assert_return(ret_path, -EINVAL); - - e = bus_label_escape(external_id); - if (!e) - return -ENOMEM; - - ret = strjoin(prefix, "/", e, NULL); - if (!ret) - return -ENOMEM; - - *ret_path = ret; - return 0; -} - -_public_ int sd_bus_path_decode(const char *path, const char *prefix, char **external_id) { - const char *e; - char *ret; - - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(object_path_is_valid(prefix), -EINVAL); - assert_return(external_id, -EINVAL); - - e = object_path_startswith(path, prefix); - if (!e) { - *external_id = NULL; - return 0; - } - - ret = bus_label_unescape(e); - if (!ret) - return -ENOMEM; - - *external_id = ret; - return 1; -} - -_public_ int sd_bus_path_encode_many(char **out, const char *path_template, ...) { - _cleanup_strv_free_ char **labels = NULL; - char *path, *path_pos, **label_pos; - const char *sep, *template_pos; - size_t path_length; - va_list list; - int r; - - assert_return(out, -EINVAL); - assert_return(path_template, -EINVAL); - - path_length = strlen(path_template); - - va_start(list, path_template); - for (sep = strchr(path_template, '%'); sep; sep = strchr(sep + 1, '%')) { - const char *arg; - char *label; - - arg = va_arg(list, const char *); - if (!arg) { - va_end(list); - return -EINVAL; - } - - label = bus_label_escape(arg); - if (!label) { - va_end(list); - return -ENOMEM; - } - - r = strv_consume(&labels, label); - if (r < 0) { - va_end(list); - return r; - } - - /* add label length, but account for the format character */ - path_length += strlen(label) - 1; - } - va_end(list); - - path = malloc(path_length + 1); - if (!path) - return -ENOMEM; - - path_pos = path; - label_pos = labels; - - for (template_pos = path_template; *template_pos; ) { - sep = strchrnul(template_pos, '%'); - path_pos = mempcpy(path_pos, template_pos, sep - template_pos); - if (!*sep) - break; - - path_pos = stpcpy(path_pos, *label_pos++); - template_pos = sep + 1; - } - - *path_pos = 0; - *out = path; - return 0; -} - -_public_ int sd_bus_path_decode_many(const char *path, const char *path_template, ...) { - _cleanup_strv_free_ char **labels = NULL; - const char *template_pos, *path_pos; - char **label_pos; - va_list list; - int r; - - /* - * This decodes an object-path based on a template argument. The - * template consists of a verbatim path, optionally including special - * directives: - * - * - Each occurrence of '%' in the template matches an arbitrary - * substring of a label in the given path. At most one such - * directive is allowed per label. For each such directive, the - * caller must provide an output parameter (char **) via va_arg. If - * NULL is passed, the given label is verified, but not returned. - * For each matched label, the *decoded* label is stored in the - * passed output argument, and the caller is responsible to free - * it. Note that the output arguments are only modified if the - * actualy path matched the template. Otherwise, they're left - * untouched. - * - * This function returns <0 on error, 0 if the path does not match the - * template, 1 if it matched. - */ - - assert_return(path, -EINVAL); - assert_return(path_template, -EINVAL); - - path_pos = path; - - for (template_pos = path_template; *template_pos; ) { - const char *sep; - size_t length; - char *label; - - /* verify everything until the next '%' matches verbatim */ - sep = strchrnul(template_pos, '%'); - length = sep - template_pos; - if (strncmp(path_pos, template_pos, length)) - return 0; - - path_pos += length; - template_pos += length; - - if (!*template_pos) - break; - - /* We found the next '%' character. Everything up until here - * matched. We now skip ahead to the end of this label and make - * sure it matches the tail of the label in the path. Then we - * decode the string in-between and save it for later use. */ - - ++template_pos; /* skip over '%' */ - - sep = strchrnul(template_pos, '/'); - length = sep - template_pos; /* length of suffix to match verbatim */ - - /* verify the suffixes match */ - sep = strchrnul(path_pos, '/'); - if (sep - path_pos < (ssize_t)length || - strncmp(sep - length, template_pos, length)) - return 0; - - template_pos += length; /* skip over matched label */ - length = sep - path_pos - length; /* length of sub-label to decode */ - - /* store unescaped label for later use */ - label = bus_label_unescape_n(path_pos, length); - if (!label) - return -ENOMEM; - - r = strv_consume(&labels, label); - if (r < 0) - return r; - - path_pos = sep; /* skip decoded label and suffix */ - } - - /* end of template must match end of path */ - if (*path_pos) - return 0; - - /* copy the labels over to the caller */ - va_start(list, path_template); - for (label_pos = labels; label_pos && *label_pos; ++label_pos) { - char **arg; - - arg = va_arg(list, char **); - if (arg) - *arg = *label_pos; - else - free(*label_pos); - } - va_end(list); - - free(labels); - labels = NULL; - return 1; -} - -_public_ int sd_bus_try_close(sd_bus *bus) { - int r; - - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!bus->is_kernel) - return -EOPNOTSUPP; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - 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; -} - -_public_ int sd_bus_get_description(sd_bus *bus, const char **description) { - assert_return(bus, -EINVAL); - assert_return(description, -EINVAL); - assert_return(bus->description, -ENXIO); - assert_return(!bus_pid_changed(bus), -ECHILD); - - *description = bus->description; - return 0; -} - -int bus_get_root_path(sd_bus *bus) { - int r; - - if (bus->cgroup_root) - return 0; - - r = cg_get_root_path(&bus->cgroup_root); - if (r == -ENOENT) { - bus->cgroup_root = strdup("/"); - if (!bus->cgroup_root) - return -ENOMEM; - - r = 0; - } - - return r; -} - -_public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) { - int r; - - assert_return(bus, -EINVAL); - assert_return(scope, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (bus->is_kernel) { - _cleanup_free_ char *n = NULL; - const char *dash; - - r = bus_kernel_get_bus_name(bus, &n); - if (r < 0) - return r; - - if (streq(n, "0-system")) { - *scope = "system"; - return 0; - } - - dash = strchr(n, '-'); - if (streq_ptr(dash, "-user")) { - *scope = "user"; - return 0; - } - } - - if (bus->is_user) { - *scope = "user"; - return 0; - } - - if (bus->is_system) { - *scope = "system"; - return 0; - } - - return -ENODATA; -} - -_public_ int sd_bus_get_address(sd_bus *bus, const char **address) { - - assert_return(bus, -EINVAL); - assert_return(address, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (bus->address) { - *address = bus->address; - return 0; - } - - return -ENODATA; -} - -_public_ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) { - assert_return(bus, -EINVAL); - assert_return(mask, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - *mask = bus->creds_mask; - return 0; -} - -_public_ int sd_bus_is_bus_client(sd_bus *bus) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return bus->bus_client; -} - -_public_ int sd_bus_is_server(sd_bus *bus) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return bus->is_server; -} - -_public_ int sd_bus_is_anonymous(sd_bus *bus) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return bus->anonymous_auth; -} - -_public_ int sd_bus_is_trusted(sd_bus *bus) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return bus->trusted; -} - -_public_ int sd_bus_is_monitor(sd_bus *bus) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return !!(bus->hello_flags & KDBUS_HELLO_MONITOR); -} - -static void flush_close(sd_bus *bus) { - if (!bus) - return; - - /* Flushes and closes the specified bus. We take a ref before, - * to ensure the flushing does not cause the bus to be - * unreferenced. */ - - sd_bus_flush_close_unref(sd_bus_ref(bus)); -} - -_public_ void sd_bus_default_flush_close(void) { - flush_close(default_starter_bus); - flush_close(default_user_bus); - flush_close(default_system_bus); -} diff --git a/src/libsystemd/sd-bus/test-bus-benchmark.c b/src/libsystemd/sd-bus/test-bus-benchmark.c deleted file mode 100644 index 56ac2ab3dd..0000000000 --- a/src/libsystemd/sd-bus/test-bus-benchmark.c +++ /dev/null @@ -1,371 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-kernel.h" -#include "bus-util.h" -#include "def.h" -#include "fd-util.h" -#include "time-util.h" -#include "util.h" - -#define MAX_SIZE (2*1024*1024) - -static usec_t arg_loop_usec = 100 * USEC_PER_MSEC; - -typedef enum Type { - TYPE_KDBUS, - TYPE_LEGACY, - TYPE_DIRECT, -} Type; - -static void server(sd_bus *b, size_t *result) { - int r; - - for (;;) { - _cleanup_(sd_bus_message_unrefp) 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_INFINITY) >= 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); - - r = sd_bus_reply_method_return(m, NULL); - assert_se(r >= 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 if (!sd_bus_message_is_signal(m, NULL, NULL)) - assert_not_reached("Unknown method"); - } -} - -static void transaction(sd_bus *b, size_t sz, const char *server_name) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; - uint8_t *p; - - assert_se(sd_bus_message_new_method_call(b, &m, server_name, "/", "benchmark.server", "Work") >= 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, const char *server_name) { - _cleanup_(sd_bus_message_unrefp) 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); - - r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL); - assert_se(r >= 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, server_name); - 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, server_name); - 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, &x, server_name, "/", "benchmark.server", "Exit") >= 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(Type type, const char *address, const char *server_name, int fd) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL; - size_t csize; - sd_bus *b; - int r; - - r = sd_bus_new(&b); - assert_se(r >= 0); - - if (type == TYPE_DIRECT) { - r = sd_bus_set_fd(b, fd, fd); - assert_se(r >= 0); - } else { - r = sd_bus_set_address(b, address); - assert_se(r >= 0); - - r = sd_bus_set_bus_client(b, true); - assert_se(r >= 0); - } - - r = sd_bus_start(b); - assert_se(r >= 0); - - r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL); - assert_se(r >= 0); - - switch (type) { - case TYPE_KDBUS: - printf("SIZE\tCOPY\tMEMFD\n"); - break; - case TYPE_LEGACY: - printf("SIZE\tLEGACY\n"); - break; - case TYPE_DIRECT: - printf("SIZE\tDIRECT\n"); - break; - } - - for (csize = 1; csize <= MAX_SIZE; csize *= 2) { - usec_t t; - unsigned n_copying, n_memfd; - - printf("%zu\t", csize); - - if (type == TYPE_KDBUS) { - b->use_memfd = 0; - - t = now(CLOCK_MONOTONIC); - for (n_copying = 0;; n_copying++) { - transaction(b, csize, server_name); - 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, server_name); - 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, &x, server_name, "/", "benchmark.server", "Exit") >= 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; - Type type = TYPE_KDBUS; - int i, pair[2] = { -1, -1 }; - _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *server_name = NULL; - _cleanup_close_ int bus_ref = -1; - const char *unique; - 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; - } else if (streq(argv[i], "legacy")) { - type = TYPE_LEGACY; - continue; - } else if (streq(argv[i], "direct")) { - type = TYPE_DIRECT; - continue; - } - - assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0); - } - - assert_se(!MODE_BISECT || TYPE_KDBUS); - - assert_se(arg_loop_usec > 0); - - if (type == TYPE_KDBUS) { - 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); - } else if (type == TYPE_LEGACY) { - const char *e; - - e = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); - assert_se(e); - - address = strdup(e); - assert_se(address); - } - - r = sd_bus_new(&b); - assert_se(r >= 0); - - if (type == TYPE_DIRECT) { - assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0); - - r = sd_bus_set_fd(b, pair[0], pair[0]); - assert_se(r >= 0); - - r = sd_bus_set_server(b, true, SD_ID128_NULL); - assert_se(r >= 0); - } else { - r = sd_bus_set_address(b, address); - assert_se(r >= 0); - - r = sd_bus_set_bus_client(b, true); - assert_se(r >= 0); - } - - r = sd_bus_start(b); - assert_se(r >= 0); - - if (type != TYPE_DIRECT) { - r = sd_bus_get_unique_name(b, &unique); - assert_se(r >= 0); - - server_name = strdup(unique); - assert_se(server_name); - } - - 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); - - safe_close(bus_ref); - sd_bus_unref(b); - - switch (mode) { - case MODE_BISECT: - client_bisect(address, server_name); - break; - - case MODE_CHART: - client_chart(type, address, server_name, pair[1]); - 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); - - safe_close(pair[1]); - sd_bus_unref(b); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c deleted file mode 100644 index 048c0d19e2..0000000000 --- a/src/libsystemd/sd-bus/test-bus-chat.c +++ /dev/null @@ -1,560 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-error.h" -#include "bus-internal.h" -#include "bus-match.h" -#include "bus-util.h" -#include "fd-util.h" -#include "formats-util.h" -#include "log.h" -#include "macro.h" -#include "util.h" - -static int match_callback(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_message *m, void *userdata, sd_bus_error *ret_error) { - int r; - - 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) - return log_error_errno(r, "Failed to send reply: %m"); - - 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_se(_bus); - - r = sd_bus_open_user(&bus); - if (r < 0) { - log_error_errno(r, "Failed to connect to user bus: %m"); - goto fail; - } - - r = sd_bus_get_bus_id(bus, &id); - if (r < 0) { - log_error_errno(r, "Failed to get server ID: %m"); - goto fail; - } - - r = sd_bus_get_unique_name(bus, &unique); - if (r < 0) { - log_error_errno(r, "Failed to get unique name: %m"); - 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_errno(r, "Failed to acquire name: %m"); - goto fail; - } - - r = sd_bus_add_fallback(bus, NULL, "/foo/bar", object_callback, NULL); - if (r < 0) { - log_error_errno(r, "Failed to add object: %m"); - goto fail; - } - - r = sd_bus_add_match(bus, NULL, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL); - if (r < 0) { - log_error_errno(r, "Failed to add match: %m"); - goto fail; - } - - r = sd_bus_add_match(bus, NULL, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL); - if (r < 0) { - log_error_errno(r, "Failed to add match: %m"); - goto fail; - } - - bus_match_dump(&bus->match_callbacks, 0); - - *_bus = bus; - return 0; - -fail: - 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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - pid_t pid = 0; - const char *label = NULL; - - r = sd_bus_process(bus, &m); - if (r < 0) { - log_error_errno(r, "Failed to process requests: %m"); - goto fail; - } - - if (r == 0) { - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) { - log_error_errno(r, "Failed to wait: %m"); - 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="PID_FMT" label=%s", - strna(sd_bus_message_get_member(m)), - 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_errno(r, "Failed to get parameter: %m"); - 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_errno(r, "Failed to send reply: %m"); - 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_errno(r, "Failed to send reply: %m"); - 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_errno(r, "Failed to send reply: %m"); - 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_errno(r, "Failed to send reply: %m"); - 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_errno(r, "Failed to get parameter: %m"); - goto fail; - } - - log_info("Received fd=%d", fd); - - if (write(fd, &x, 1) < 0) { - log_error_errno(errno, "Failed to write to fd: %m"); - safe_close(fd); - goto fail; - } - - r = sd_bus_reply_method_return(m, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - 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_errno(r, "Failed to send reply: %m"); - goto fail; - } - } - } - - r = 0; - -fail: - if (bus) { - sd_bus_flush(bus); - sd_bus_unref(bus); - } - - return r; -} - -static void* client1(void*p) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *hello; - int r; - _cleanup_close_pair_ int pp[2] = { -1, -1 }; - char x; - - r = sd_bus_open_user(&bus); - if (r < 0) { - log_error_errno(r, "Failed to connect to user bus: %m"); - 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_errno(r, "Failed to issue method call: %m"); - goto finish; - } - - r = sd_bus_message_read(reply, "s", &hello); - if (r < 0) { - log_error_errno(r, "Failed to get string: %m"); - goto finish; - } - - assert_se(streq(hello, "hello")); - - if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) { - log_error_errno(errno, "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_errno(r, "Failed to issue method call: %m"); - 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_(sd_bus_message_unrefp) sd_bus_message *q; - - r = sd_bus_message_new_method_call( - bus, - &q, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.systemd.test", - "ExitClient1"); - if (r < 0) - log_error_errno(r, "Failed to allocate method call: %m"); - else - sd_bus_send(bus, q, NULL); - - } - - return INT_TO_PTR(r); -} - -static int quit_callback(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_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_(sd_bus_error_free) 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_errno(r, "Failed to connect to user bus: %m"); - goto finish; - } - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd.test", - "/foo/bar/waldo/piep", - "org.object.test", - "Foobar"); - if (r < 0) { - log_error_errno(r, "Failed to allocate method call: %m"); - 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; - } - - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_signal( - bus, - &m, - "/foobar", - "foo.bar", - "Notify"); - if (r < 0) { - log_error_errno(r, "Failed to allocate signal: %m"); - 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; - } - - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.DBus.Peer", - "GetMachineId"); - if (r < 0) { - log_error_errno(r, "Failed to allocate method call: %m"); - 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_errno(r, "Failed to parse machine ID: %m"); - goto finish; - } - - log_info("Machine ID is %s.", mid); - - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.systemd.test", - "Slow"); - if (r < 0) { - log_error_errno(r, "Failed to allocate method call: %m"); - goto finish; - } - - reply = sd_bus_message_unref(reply); - - 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."); - - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.systemd.test", - "Slow"); - if (r < 0) { - log_error_errno(r, "Failed to allocate method call: %m"); - goto finish; - } - - r = sd_bus_call_async(bus, NULL, m, quit_callback, &quit, 200 * USEC_PER_MSEC); - 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_errno(r, "Failed to process requests: %m"); - goto finish; - } - if (r == 0) { - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) { - log_error_errno(r, "Failed to wait: %m"); - goto finish; - } - } - } - - r = 0; - -finish: - if (bus) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *q; - - r = sd_bus_message_new_method_call( - bus, - &q, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.systemd.test", - "ExitClient2"); - if (r < 0) { - log_error_errno(r, "Failed to allocate method call: %m"); - goto finish; - } - - (void) sd_bus_send(bus, q, NULL); - } - - 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 deleted file mode 100644 index 250a5b2908..0000000000 --- a/src/libsystemd/sd-bus/test-bus-cleanup.c +++ /dev/null @@ -1,95 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "sd-bus.h" - -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-util.h" -#include "refcnt.h" - -static void test_bus_new(void) { - _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; - - assert_se(sd_bus_new(&bus) == 0); - printf("after new: refcount %u\n", REFCNT_GET(bus->n_ref)); -} - -static int test_bus_open(void) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - r = sd_bus_open_system(&bus); - if (r == -ECONNREFUSED || r == -ENOENT) - return r; - - assert_se(r >= 0); - printf("after open: refcount %u\n", REFCNT_GET(bus->n_ref)); - - return 0; -} - -static void test_bus_new_method_call(void) { - sd_bus *bus = NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - - assert_se(sd_bus_open_system(&bus) >= 0); - - assert_se(sd_bus_message_new_method_call(bus, &m, "a.service.name", "/an/object/path", "an.interface.name", "AMethodName") >= 0); - - printf("after message_new_method_call: refcount %u\n", REFCNT_GET(bus->n_ref)); - - sd_bus_flush_close_unref(bus); - printf("after bus_flush_close_unref: refcount %u\n", m->n_ref); -} - -static void test_bus_new_signal(void) { - sd_bus *bus = NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - - assert_se(sd_bus_open_system(&bus) >= 0); - - assert_se(sd_bus_message_new_signal(bus, &m, "/an/object/path", "an.interface.name", "Name") >= 0); - - printf("after message_new_signal: refcount %u\n", REFCNT_GET(bus->n_ref)); - - sd_bus_flush_close_unref(bus); - printf("after bus_flush_close_unref: refcount %u\n", m->n_ref); -} - -int main(int argc, char **argv) { - int r; - - log_parse_environment(); - log_open(); - - test_bus_new(); - r = test_bus_open(); - if (r < 0) { - log_info("Failed to connect to bus, skipping tests."); - return EXIT_TEST_SKIP; - } - - test_bus_new_method_call(); - test_bus_new_signal(); - - return EXIT_SUCCESS; -} diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c deleted file mode 100644 index e9ef483bdd..0000000000 --- a/src/libsystemd/sd-bus/test-bus-creds.c +++ /dev/null @@ -1,50 +0,0 @@ -/*** - 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 . -***/ - -#include "sd-bus.h" - -#include "bus-dump.h" -#include "bus-util.h" -#include "cgroup-util.h" - -int main(int argc, char *argv[]) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - int r; - - if (cg_unified() == -ENOMEDIUM) { - puts("Skipping test: /sys/fs/cgroup/ not available"); - return EXIT_TEST_SKIP; - } - - r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL); - assert_se(r >= 0); - - bus_creds_dump(creds, NULL, true); - - creds = sd_bus_creds_unref(creds); - - r = sd_bus_creds_new_from_pid(&creds, 1, _SD_BUS_CREDS_ALL); - if (r != -EACCES) { - assert_se(r >= 0); - putchar('\n'); - bus_creds_dump(creds, NULL, true); - } - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c deleted file mode 100644 index 66a3874f10..0000000000 --- a/src/libsystemd/sd-bus/test-bus-error.c +++ /dev/null @@ -1,232 +0,0 @@ -/*** - 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 . -***/ - -#include "sd-bus.h" - -#include "bus-common-errors.h" -#include "bus-error.h" -#include "bus-util.h" -#include "errno-list.h" - -static void test_error(void) { - _cleanup_(sd_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") == -EOPNOTSUPP); - 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) == EOPNOTSUPP); - assert_se(sd_bus_error_is_set(&error)); - sd_bus_error_free(&error); - - /* Check with no error */ - assert_se(!sd_bus_error_is_set(&error)); - assert_se(sd_bus_error_setf(&error, NULL, "yyy %i", -1) == 0); - assert_se(error.name == NULL); - assert_se(error.message == NULL); - assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND)); - assert_se(sd_bus_error_get_errno(&error) == 0); - 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)); - sd_bus_error_free(&error); - - /* Check with no error */ - assert_se(!sd_bus_error_is_set(&error)); - assert_se(sd_bus_error_set_errnof(&error, 0, "Waldi %c", 'X') == 0); - assert_se(error.name == NULL); - assert_se(error.message == NULL); - assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR)); - assert_se(sd_bus_error_get_errno(&error) == 0); - assert_se(!sd_bus_error_is_set(&error)); -} - -extern const sd_bus_error_map __start_BUS_ERROR_MAP[]; -extern const sd_bus_error_map __stop_BUS_ERROR_MAP[]; - -static void dump_mapping_table(void) { - const sd_bus_error_map *m; - - printf("----- errno mappings ------\n"); - m = __start_BUS_ERROR_MAP; - while (m < __stop_BUS_ERROR_MAP) { - - if (m->code == BUS_ERROR_MAP_END_MARKER) { - m = ALIGN8_PTR(m+1); - continue; - } - - printf("%s -> %i/%s\n", strna(m->name), m->code, strna(errno_to_name(m->code))); - m++; - } - printf("---------------------------\n"); -} - -static void test_errno_mapping_standard(void) { - assert_se(sd_bus_error_set(NULL, "System.Error.EUCLEAN", NULL) == -EUCLEAN); - assert_se(sd_bus_error_set(NULL, "System.Error.EBUSY", NULL) == -EBUSY); - assert_se(sd_bus_error_set(NULL, "System.Error.EINVAL", NULL) == -EINVAL); - assert_se(sd_bus_error_set(NULL, "System.Error.WHATSIT", NULL) == -EIO); -} - -BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors[] = { - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error", 5), - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-2", 52), - SD_BUS_ERROR_MAP_END -}; - -BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors2[] = { - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-3", 33), - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-4", 44), - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-33", 333), - SD_BUS_ERROR_MAP_END -}; - -static const sd_bus_error_map test_errors3[] = { - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-88", 888), - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-99", 999), - SD_BUS_ERROR_MAP_END -}; - -static const sd_bus_error_map test_errors4[] = { - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-77", 777), - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-78", 778), - SD_BUS_ERROR_MAP_END -}; - -static const sd_bus_error_map test_errors_bad1[] = { - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-1", 0), - SD_BUS_ERROR_MAP_END -}; - -static const sd_bus_error_map test_errors_bad2[] = { - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-1", -1), - SD_BUS_ERROR_MAP_END -}; - -static void test_errno_mapping_custom(void) { - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error", NULL) == -5); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-x", NULL) == -EIO); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-33", NULL) == -333); - - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -EIO); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -EIO); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -EIO); - - assert_se(sd_bus_error_add_map(test_errors3) > 0); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -888); - assert_se(sd_bus_error_add_map(test_errors4) > 0); - assert_se(sd_bus_error_add_map(test_errors4) == 0); - assert_se(sd_bus_error_add_map(test_errors3) == 0); - - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -999); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -777); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-78", NULL) == -778); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-y", NULL) == -EIO); - - assert_se(sd_bus_error_set(NULL, BUS_ERROR_NO_SUCH_UNIT, NULL) == -ENOENT); - - assert_se(sd_bus_error_add_map(test_errors_bad1) == -EINVAL); - assert_se(sd_bus_error_add_map(test_errors_bad2) == -EINVAL); -} - -int main(int argc, char *argv[]) { - dump_mapping_table(); - - test_error(); - test_errno_mapping_standard(); - test_errno_mapping_custom(); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-gvariant.c b/src/libsystemd/sd-bus/test-bus-gvariant.c deleted file mode 100644 index 83f114a0fe..0000000000 --- a/src/libsystemd/sd-bus/test-bus-gvariant.c +++ /dev/null @@ -1,224 +0,0 @@ -/*** - 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 . -***/ - -#ifdef HAVE_GLIB -#include -#endif - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-dump.h" -#include "bus-gvariant.h" -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-util.h" -#include "macro.h" -#include "util.h" - -static void test_bus_gvariant_is_fixed_size(void) { - assert_se(bus_gvariant_is_fixed_size("") > 0); - assert_se(bus_gvariant_is_fixed_size("()") > 0); - assert_se(bus_gvariant_is_fixed_size("y") > 0); - assert_se(bus_gvariant_is_fixed_size("u") > 0); - assert_se(bus_gvariant_is_fixed_size("b") > 0); - assert_se(bus_gvariant_is_fixed_size("n") > 0); - assert_se(bus_gvariant_is_fixed_size("q") > 0); - assert_se(bus_gvariant_is_fixed_size("i") > 0); - assert_se(bus_gvariant_is_fixed_size("t") > 0); - assert_se(bus_gvariant_is_fixed_size("d") > 0); - assert_se(bus_gvariant_is_fixed_size("s") == 0); - assert_se(bus_gvariant_is_fixed_size("o") == 0); - assert_se(bus_gvariant_is_fixed_size("g") == 0); - assert_se(bus_gvariant_is_fixed_size("h") > 0); - assert_se(bus_gvariant_is_fixed_size("ay") == 0); - assert_se(bus_gvariant_is_fixed_size("v") == 0); - assert_se(bus_gvariant_is_fixed_size("(u)") > 0); - assert_se(bus_gvariant_is_fixed_size("(uuuuy)") > 0); - assert_se(bus_gvariant_is_fixed_size("(uusuuy)") == 0); - assert_se(bus_gvariant_is_fixed_size("a{ss}") == 0); - assert_se(bus_gvariant_is_fixed_size("((u)yyy(b(iiii)))") > 0); - assert_se(bus_gvariant_is_fixed_size("((u)yyy(b(iiivi)))") == 0); -} - -static void test_bus_gvariant_get_size(void) { - assert_se(bus_gvariant_get_size("") == 0); - assert_se(bus_gvariant_get_size("()") == 1); - assert_se(bus_gvariant_get_size("y") == 1); - assert_se(bus_gvariant_get_size("u") == 4); - assert_se(bus_gvariant_get_size("b") == 1); - assert_se(bus_gvariant_get_size("n") == 2); - assert_se(bus_gvariant_get_size("q") == 2); - assert_se(bus_gvariant_get_size("i") == 4); - assert_se(bus_gvariant_get_size("t") == 8); - assert_se(bus_gvariant_get_size("d") == 8); - assert_se(bus_gvariant_get_size("s") < 0); - assert_se(bus_gvariant_get_size("o") < 0); - assert_se(bus_gvariant_get_size("g") < 0); - assert_se(bus_gvariant_get_size("h") == 4); - assert_se(bus_gvariant_get_size("ay") < 0); - assert_se(bus_gvariant_get_size("v") < 0); - assert_se(bus_gvariant_get_size("(u)") == 4); - assert_se(bus_gvariant_get_size("(uuuuy)") == 20); - assert_se(bus_gvariant_get_size("(uusuuy)") < 0); - assert_se(bus_gvariant_get_size("a{ss}") < 0); - assert_se(bus_gvariant_get_size("((u)yyy(b(iiii)))") == 28); - assert_se(bus_gvariant_get_size("((u)yyy(b(iiivi)))") < 0); - assert_se(bus_gvariant_get_size("((b)(t))") == 16); - assert_se(bus_gvariant_get_size("((b)(b)(t))") == 16); - assert_se(bus_gvariant_get_size("(bt)") == 16); - assert_se(bus_gvariant_get_size("((t)(b))") == 16); - assert_se(bus_gvariant_get_size("(tb)") == 16); - assert_se(bus_gvariant_get_size("((b)(b))") == 2); - assert_se(bus_gvariant_get_size("((t)(t))") == 16); -} - -static void test_bus_gvariant_get_alignment(void) { - assert_se(bus_gvariant_get_alignment("") == 1); - assert_se(bus_gvariant_get_alignment("()") == 1); - assert_se(bus_gvariant_get_alignment("y") == 1); - assert_se(bus_gvariant_get_alignment("b") == 1); - assert_se(bus_gvariant_get_alignment("u") == 4); - assert_se(bus_gvariant_get_alignment("s") == 1); - assert_se(bus_gvariant_get_alignment("o") == 1); - assert_se(bus_gvariant_get_alignment("g") == 1); - assert_se(bus_gvariant_get_alignment("v") == 8); - assert_se(bus_gvariant_get_alignment("h") == 4); - assert_se(bus_gvariant_get_alignment("i") == 4); - assert_se(bus_gvariant_get_alignment("t") == 8); - assert_se(bus_gvariant_get_alignment("x") == 8); - assert_se(bus_gvariant_get_alignment("q") == 2); - assert_se(bus_gvariant_get_alignment("n") == 2); - assert_se(bus_gvariant_get_alignment("d") == 8); - assert_se(bus_gvariant_get_alignment("ay") == 1); - assert_se(bus_gvariant_get_alignment("as") == 1); - assert_se(bus_gvariant_get_alignment("au") == 4); - assert_se(bus_gvariant_get_alignment("an") == 2); - assert_se(bus_gvariant_get_alignment("ans") == 2); - assert_se(bus_gvariant_get_alignment("ant") == 8); - assert_se(bus_gvariant_get_alignment("(ss)") == 1); - assert_se(bus_gvariant_get_alignment("(ssu)") == 4); - assert_se(bus_gvariant_get_alignment("a(ssu)") == 4); - assert_se(bus_gvariant_get_alignment("(u)") == 4); - assert_se(bus_gvariant_get_alignment("(uuuuy)") == 4); - assert_se(bus_gvariant_get_alignment("(uusuuy)") == 4); - assert_se(bus_gvariant_get_alignment("a{ss}") == 1); - assert_se(bus_gvariant_get_alignment("((u)yyy(b(iiii)))") == 4); - assert_se(bus_gvariant_get_alignment("((u)yyy(b(iiivi)))") == 8); - assert_se(bus_gvariant_get_alignment("((b)(t))") == 8); - assert_se(bus_gvariant_get_alignment("((b)(b)(t))") == 8); - assert_se(bus_gvariant_get_alignment("(bt)") == 8); - assert_se(bus_gvariant_get_alignment("((t)(b))") == 8); - assert_se(bus_gvariant_get_alignment("(tb)") == 8); - assert_se(bus_gvariant_get_alignment("((b)(b))") == 1); - assert_se(bus_gvariant_get_alignment("((t)(t))") == 8); -} - -static void test_marshal(void) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *n = NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_free_ void *blob; - size_t sz; - int r; - - r = sd_bus_open_system(&bus); - if (r < 0) - exit(EXIT_TEST_SKIP); - - bus->message_version = 2; /* dirty hack to enable gvariant */ - - assert_se(sd_bus_message_new_method_call(bus, &m, "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") >= 0); - - assert_cc(sizeof(struct bus_header) == 16); - - 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("(yyyyuta{tv})"), m->header, sizeof(struct bus_header) + m->fields_size, false, NULL, NULL); - assert_se(g_variant_is_normal_form(v)); - 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, m->user_body_size, false, NULL, NULL); - assert_se(g_variant_is_normal_form(v)); - 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, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); - - assert_se(bus_message_get_blob(m, &blob, &sz) >= 0); - -#ifdef HAVE_GLIB - { - GVariant *v; - char *t; - - v = g_variant_new_from_data(G_VARIANT_TYPE("(yyyyuta{tv}v)"), blob, sz, false, NULL, NULL); - assert_se(g_variant_is_normal_form(v)); - t = g_variant_print(v, TRUE); - printf("%s\n", t); - g_free(t); - g_variant_unref(v); - } -#endif - - assert_se(bus_message_from_malloc(bus, blob, sz, NULL, 0, NULL, &n) >= 0); - blob = NULL; - - assert_se(bus_message_dump(n, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); - - m = sd_bus_message_unref(m); - - assert_se(sd_bus_message_new_method_call(bus, &m, "a.x", "/a/x", "a.x", "Ax") >= 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, BUS_MESSAGE_DUMP_WITH_HEADER) >= 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 deleted file mode 100644 index 4425cfae26..0000000000 --- a/src/libsystemd/sd-bus/test-bus-introspect.c +++ /dev/null @@ -1,63 +0,0 @@ -/*** - 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 . -***/ - -#include "bus-introspect.h" -#include "log.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_PROPERTY_EXPLICIT), - 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, " \n"); - assert_se(introspect_write_interface(&intro, vtable) >= 0); - fputs(" \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-bloom.c b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c deleted file mode 100644 index eb6179d7d2..0000000000 --- a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c +++ /dev/null @@ -1,141 +0,0 @@ -/*** - 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 . -***/ - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-kernel.h" -#include "bus-util.h" -#include "fd-util.h" -#include "log.h" -#include "util.h" - -static int test_match(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - int *found = userdata; - - *found = 1; - - return 0; -} - -static void test_one( - const char *path, - const char *interface, - const char *member, - bool as_list, - 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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - sd_bus *a, *b; - int r, found = 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(&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, NULL, match, test_match, &found); - assert_se(r >= 0); - - log_debug("signal"); - - if (as_list) - r = sd_bus_emit_signal(a, path, interface, member, "as", 1, arg0); - else - 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 == !!found); - - 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", false, "foobar", "", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/tuut'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "interface='waldo.com'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "member='Piep'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "member='Pi_ep'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "arg0='foobar'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "arg0='foo_bar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foobar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foo_bar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0has='foobar'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0has='foo_bar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar2'", false); - - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/quux'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar/waldo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/quux'", false); - test_one("/", "waldo.com", "Piep", false, "foobar", "path_namespace='/'", true); - - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar/waldo/'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/'", true); - - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo/bar/waldo", "arg0path='/foo/'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo", "arg0path='/foo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo", "arg0path='/foo/bar/waldo'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo/", "arg0path='/foo/bar/waldo'", true); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-kernel.c b/src/libsystemd/sd-bus/test-bus-kernel.c deleted file mode 100644 index 2214817312..0000000000 --- a/src/libsystemd/sd-bus/test-bus-kernel.c +++ /dev/null @@ -1,190 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-dump.h" -#include "bus-kernel.h" -#include "bus-util.h" -#include "fd-util.h" -#include "log.h" -#include "util.h" - -int main(int argc, char *argv[]) { - _cleanup_close_ int bus_ref = -1; - _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *bname = NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_(sd_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]; - const char *nn; - - 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_description(a, "a"); - 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_timestamp(a, 1) >= 0); - assert_se(sd_bus_negotiate_creds(a, true, _SD_BUS_CREDS_ALL) >= 0); - - assert_se(sd_bus_negotiate_timestamp(b, 0) >= 0); - assert_se(sd_bus_negotiate_creds(b, true, 0) >= 0); - - r = sd_bus_start(a); - assert_se(r >= 0); - - r = sd_bus_start(b); - assert_se(r >= 0); - - assert_se(sd_bus_negotiate_timestamp(b, 1) >= 0); - assert_se(sd_bus_negotiate_creds(b, true, _SD_BUS_CREDS_ALL) >= 0); - - r = sd_bus_get_unique_name(a, &ua); - assert_se(r >= 0); - printf("unique a: %s\n", ua); - - r = sd_bus_get_description(a, &nn); - assert_se(r >= 0); - printf("name of a: %s\n", nn); - - r = sd_bus_get_unique_name(b, &ub); - assert_se(r >= 0); - printf("unique b: %s\n", ub); - - r = sd_bus_get_description(b, &nn); - assert_se(r >= 0); - printf("name of b: %s\n", nn); - - assert_se(bus_kernel_get_bus_name(b, &bname) >= 0); - assert_se(endswith(bname, name)); - - 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, NULL, "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_priority(b, -10, &m); - assert_se(r == 0); - - r = sd_bus_process(b, &m); - assert_se(r > 0); - assert_se(m); - - bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - 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, &m, "net.x0pointer.foobar", "/a/path", "an.inter.face", "AMethod"); - assert_se(r >= 0); - - assert_se(pipe2(pipe_fds, O_CLOEXEC) >= 0); - - assert_se(write(pipe_fds[1], "x", 1) == 1); - - pipe_fds[1] = safe_close(pipe_fds[1]); - - r = sd_bus_message_append(m, "h", pipe_fds[0]); - assert_se(r >= 0); - - pipe_fds[0] = safe_close(pipe_fds[0]); - - 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, BUS_MESSAGE_DUMP_WITH_HEADER); - 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 deleted file mode 100644 index a28cc5b79e..0000000000 --- a/src/libsystemd/sd-bus/test-bus-marshal.c +++ /dev/null @@ -1,432 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#ifdef HAVE_GLIB -#include -#endif - -#ifdef HAVE_DBUS -#include -#endif - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-dump.h" -#include "bus-label.h" -#include "bus-message.h" -#include "bus-util.h" -#include "fd-util.h" -#include "hexdecoct.h" -#include "log.h" -#include "util.h" - -static void test_bus_path_encode_unique(void) { - _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL; - - assert_se(bus_path_encode_unique(NULL, "/foo/bar", "some.sender", "a.suffix", &a) >= 0 && streq_ptr(a, "/foo/bar/some_2esender/a_2esuffix")); - assert_se(bus_path_decode_unique(a, "/foo/bar", &b, &c) > 0 && streq_ptr(b, "some.sender") && streq_ptr(c, "a.suffix")); - assert_se(bus_path_decode_unique(a, "/bar/foo", &d, &d) == 0 && !d); - assert_se(bus_path_decode_unique("/foo/bar/onlyOneSuffix", "/foo/bar", &d, &d) == 0 && !d); - assert_se(bus_path_decode_unique("/foo/bar/_/_", "/foo/bar", &d, &e) > 0 && streq_ptr(d, "") && streq_ptr(e, "")); -} - -static void test_bus_path_encode(void) { - _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *f = NULL; - - assert_se(sd_bus_path_encode("/foo/bar", "waldo", &a) >= 0 && streq(a, "/foo/bar/waldo")); - assert_se(sd_bus_path_decode(a, "/waldo", &b) == 0 && b == NULL); - assert_se(sd_bus_path_decode(a, "/foo/bar", &b) > 0 && streq(b, "waldo")); - - assert_se(sd_bus_path_encode("xxxx", "waldo", &c) < 0); - assert_se(sd_bus_path_encode("/foo/", "waldo", &c) < 0); - - assert_se(sd_bus_path_encode("/foo/bar", "", &c) >= 0 && streq(c, "/foo/bar/_")); - assert_se(sd_bus_path_decode(c, "/foo/bar", &d) > 0 && streq(d, "")); - - assert_se(sd_bus_path_encode("/foo/bar", "foo.bar", &e) >= 0 && streq(e, "/foo/bar/foo_2ebar")); - assert_se(sd_bus_path_decode(e, "/foo/bar", &f) > 0 && streq(f, "foo.bar")); -} - -static void test_bus_path_encode_many(void) { - _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *f = NULL; - - assert_se(sd_bus_path_decode_many("/foo/bar", "/prefix/%", NULL) == 0); - assert_se(sd_bus_path_decode_many("/prefix/bar", "/prefix/%bar", NULL) == 1); - assert_se(sd_bus_path_decode_many("/foo/bar", "/prefix/%/suffix", NULL) == 0); - assert_se(sd_bus_path_decode_many("/prefix/foobar/suffix", "/prefix/%/suffix", &a) == 1 && streq_ptr(a, "foobar")); - assert_se(sd_bus_path_decode_many("/prefix/one_foo_two/mid/three_bar_four/suffix", "/prefix/one_%_two/mid/three_%_four/suffix", &b, &c) == 1 && streq_ptr(b, "foo") && streq_ptr(c, "bar")); - assert_se(sd_bus_path_decode_many("/prefix/one_foo_two/mid/three_bar_four/suffix", "/prefix/one_%_two/mid/three_%_four/suffix", NULL, &d) == 1 && streq_ptr(d, "bar")); - - assert_se(sd_bus_path_decode_many("/foo/bar", "/foo/bar/%", NULL) == 0); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/bar%", NULL) == 0); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/bar", NULL) == 0); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%bar", NULL) == 0); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/bar/suffix") == 1); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%%/suffix", NULL, NULL) == 0); /* multiple '%' are treated verbatim */ - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/suffi", NULL) == 0); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/suffix", &e) == 1 && streq_ptr(e, "bar")); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/%", NULL, NULL) == 1); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%/%", NULL, NULL, NULL) == 1); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "%/%/%", NULL, NULL, NULL) == 0); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%", NULL, NULL) == 0); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%/", NULL, NULL) == 0); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/", NULL) == 0); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%", NULL) == 0); - assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "%", NULL) == 0); - - assert_se(sd_bus_path_encode_many(&f, "/prefix/one_%_two/mid/three_%_four/suffix", "foo", "bar") >= 0 && streq_ptr(f, "/prefix/one_foo_two/mid/three_bar_four/suffix")); -} - -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 = bus_label_escape(a)); - assert_se(streq(t, b)); - - assert_se(x = bus_label_unescape(t)); - assert_se(streq(a, x)); - - assert_se(y = 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_(sd_bus_message_unrefp) 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; - _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; - double dbl; - uint64_t u64; - - r = sd_bus_default_system(&bus); - if (r < 0) - return EXIT_TEST_SKIP; - - r = sd_bus_message_new_method_call(bus, &m, "foobar.waldo", "/", "foobar.waldo", "Piep"); - assert_se(r >= 0); - - r = sd_bus_message_append(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, "y(ty)y(yt)y", 8, 777ULL, 7, 9, 77, 7777ULL, 10); - assert_se(r >= 0); - - r = sd_bus_message_append(m, "()"); - 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 = sd_bus_message_append(m, "a(stdo)", 1, "foo", 815ULL, 47.0, "/"); - assert_se(r >= 0); - - r = bus_message_seal(m, 4711, 0); - assert_se(r >= 0); - - bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - ms = open_memstream(&first, &first_size); - bus_message_dump(m, ms, 0); - 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 = %zu, contents =\n%s", 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); - - dbus_error_free(&error); - } -#endif - - m = sd_bus_message_unref(m); - - r = bus_message_from_malloc(bus, buffer, sz, NULL, 0, NULL, &m); - assert_se(r >= 0); - - bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - fclose(ms); - ms = open_memstream(&second, &second_size); - bus_message_dump(m, ms, 0); - 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, "y(ty)", &v, &u64, &u); - assert_se(r > 0); - assert_se(v == 8); - assert_se(u64 == 777); - assert_se(u == 7); - - r = sd_bus_message_read(m, "y(yt)", &v, &u, &u64); - assert_se(r > 0); - assert_se(v == 9); - assert_se(u == 77); - assert_se(u64 == 7777); - - r = sd_bus_message_read(m, "y", &v); - assert_se(r > 0); - assert_se(v == 10); - - r = sd_bus_message_read(m, "()"); - assert_se(r > 0); - - 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_read(m, "a(stdo)", 1, &x, &u64, &dbl, &y); - assert_se(r > 0); - assert_se(streq(x, "foo")); - assert_se(u64 == 815ULL); - assert_se(fabs(dbl - 47.0) < 0.1); - assert_se(streq(y, "/")); - - r = sd_bus_message_peek_type(m, NULL, NULL); - assert_se(r == 0); - - r = sd_bus_message_new_method_call(bus, ©, "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, 0); - 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}y(ty)y(yt)y()"); - 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(); - test_bus_path_encode(); - test_bus_path_encode_unique(); - test_bus_path_encode_many(); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-match.c b/src/libsystemd/sd-bus/test-bus-match.c deleted file mode 100644 index 29c4529f95..0000000000 --- a/src/libsystemd/sd-bus/test-bus-match.c +++ /dev/null @@ -1,159 +0,0 @@ -/*** - 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 . -***/ - -#include "bus-match.h" -#include "bus-message.h" -#include "bus-slot.h" -#include "bus-util.h" -#include "log.h" -#include "macro.h" - -static bool mask[32]; - -static int filter(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - log_info("Ran %u", PTR_TO_UINT(userdata)); - assert_se(PTR_TO_UINT(userdata) < ELEMENTSOF(mask)); - mask[PTR_TO_UINT(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(sd_bus_slot *slots, struct bus_match_node *root, const char *match, int value) { - struct bus_match_component *components = NULL; - unsigned n_components = 0; - sd_bus_slot *s; - int r; - - s = slots + value; - zero(*s); - - r = bus_match_parse(match, &components, &n_components); - if (r < 0) - return r; - - s->userdata = INT_TO_PTR(value); - s->match_callback.callback = filter; - - r = bus_match_add(root, components, n_components, &s->match_callback); - bus_match_parse_free(components, n_components); - - return r; -} - -static void test_match_scope(const char *match, enum bus_match_scope scope) { - struct bus_match_component *components = NULL; - unsigned n_components = 0; - - assert_se(bus_match_parse(match, &components, &n_components) >= 0); - assert_se(bus_match_get_scope(components, n_components) == scope); - bus_match_parse_free(components, n_components); -} - -int main(int argc, char *argv[]) { - struct bus_match_node root = { - .type = BUS_MATCH_ROOT, - }; - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - enum bus_match_node_type i; - sd_bus_slot slots[19]; - int r; - - r = sd_bus_open_system(&bus); - if (r < 0) - return EXIT_TEST_SKIP; - - assert_se(match_add(slots, &root, "arg2='wal\\'do',sender='foo',type='signal',interface='bar.x',", 1) >= 0); - assert_se(match_add(slots, &root, "arg2='wal\\'do2',sender='foo',type='signal',interface='bar.x',", 2) >= 0); - assert_se(match_add(slots, &root, "arg3='test',sender='foo',type='signal',interface='bar.x',", 3) >= 0); - assert_se(match_add(slots, &root, "arg3='test',sender='foo',type='method_call',interface='bar.x',", 4) >= 0); - assert_se(match_add(slots, &root, "", 5) >= 0); - assert_se(match_add(slots, &root, "interface='quux.x'", 6) >= 0); - assert_se(match_add(slots, &root, "interface='bar.x'", 7) >= 0); - assert_se(match_add(slots, &root, "member='waldo',path='/foo/bar'", 8) >= 0); - assert_se(match_add(slots, &root, "path='/foo/bar'", 9) >= 0); - assert_se(match_add(slots, &root, "path_namespace='/foo'", 10) >= 0); - assert_se(match_add(slots, &root, "path_namespace='/foo/quux'", 11) >= 0); - assert_se(match_add(slots, &root, "arg1='two'", 12) >= 0); - assert_se(match_add(slots, &root, "member='waldo',arg2path='/prefix/'", 13) >= 0); - assert_se(match_add(slots, &root, "member=waldo,path='/foo/bar',arg3namespace='prefix'", 14) >= 0); - assert_se(match_add(slots, &root, "arg4has='pi'", 15) >= 0); - assert_se(match_add(slots, &root, "arg4has='pa'", 16) >= 0); - assert_se(match_add(slots, &root, "arg4has='po'", 17) >= 0); - assert_se(match_add(slots, &root, "arg4='pi'", 18) >= 0); - - bus_match_dump(&root, 0); - - assert_se(sd_bus_message_new_signal(bus, &m, "/foo/bar", "bar.x", "waldo") >= 0); - assert_se(sd_bus_message_append(m, "ssssas", "one", "two", "/prefix/three", "prefix.four", 3, "pi", "pa", "po") >= 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, 15, 16, 17 }, 11)); - - assert_se(bus_match_remove(&root, &slots[8].match_callback) >= 0); - assert_se(bus_match_remove(&root, &slots[13].match_callback) >= 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, 15, 16, 17 }, 9)); - - 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); - - test_match_scope("interface='foobar'", BUS_MATCH_GENERIC); - test_match_scope("", BUS_MATCH_GENERIC); - test_match_scope("interface='org.freedesktop.DBus.Local'", BUS_MATCH_LOCAL); - test_match_scope("sender='org.freedesktop.DBus.Local'", BUS_MATCH_LOCAL); - test_match_scope("member='gurke',path='/org/freedesktop/DBus/Local'", BUS_MATCH_LOCAL); - test_match_scope("arg2='piep',sender='org.freedesktop.DBus',member='waldo'", BUS_MATCH_DRIVER); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c deleted file mode 100644 index f11cafd888..0000000000 --- a/src/libsystemd/sd-bus/test-bus-objects.c +++ /dev/null @@ -1,555 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-dump.h" -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-util.h" -#include "log.h" -#include "macro.h" -#include "strv.h" -#include "util.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_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_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_message *m, void *userdata, sd_bus_error *error) { - int r; - - assert_se(sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), 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_message *m, void *userdata, sd_bus_error *error) { - int r; - - assert_se(sd_bus_emit_properties_changed_strv(sd_bus_message_get_bus(m), 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_message *m, void *userdata, sd_bus_error *error) { - int r; - - assert_se(sd_bus_emit_interfaces_added(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0); - - r = sd_bus_reply_method_return(m, NULL); - assert_se(r >= 0); - - return 1; -} - -static int emit_interfaces_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int r; - - assert_se(sd_bus_emit_interfaces_removed(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0); - - r = sd_bus_reply_method_return(m, NULL); - assert_se(r >= 0); - - return 1; -} - -static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int r; - - assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), "/value/a/x") >= 0); - - r = sd_bus_reply_method_return(m, NULL); - assert_se(r >= 0); - - return 1; -} - -static int emit_object_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int r; - - assert_se(sd_bus_emit_object_removed(sd_bus_message_get_bus(m), "/value/a/x") >= 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_METHOD("EmitObjectAdded", NULL, NULL, emit_object_added, 0), - SD_BUS_METHOD("EmitObjectRemoved", NULL, NULL, emit_object_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_PROPERTY("AnExplicitProperty", "s", NULL, offsetof(struct context, something), SD_BUS_VTABLE_PROPERTY_EXPLICIT|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - SD_BUS_VTABLE_END -}; - -static int enumerator_callback(sd_bus *bus, 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 int enumerator2_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - - if (object_path_startswith("/value/a", path)) - assert_se(*nodes = strv_new("/value/a/x", "/value/a/y", "/value/a/z", 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, NULL, "/foo", "org.freedesktop.systemd.test", vtable, c) >= 0); - assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0); - assert_se(sd_bus_add_fallback_vtable(bus, NULL, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0); - assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value", enumerator_callback, NULL) >= 0); - assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value/a", enumerator2_callback, NULL) >= 0); - assert_se(sd_bus_add_object_manager(bus, NULL, "/value") >= 0); - assert_se(sd_bus_add_object_manager(bus, NULL, "/value/a") >= 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_errno(r, "Failed to process requests: %m"); - goto fail; - } - - if (r == 0) { - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) { - log_error_errno(r, "Failed to wait: %m"); - 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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; - _cleanup_(sd_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, "<<>>")); - - 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, "<<>>")); - - 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, BUS_MESSAGE_DUMP_WITH_HEADER); - - 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, BUS_MESSAGE_DUMP_WITH_HEADER); - - 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, BUS_MESSAGE_DUMP_WITH_HEADER); - - 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, BUS_MESSAGE_DUMP_WITH_HEADER); - - 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, BUS_MESSAGE_DUMP_WITH_HEADER); - - 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, BUS_MESSAGE_DUMP_WITH_HEADER); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectAdded", &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, BUS_MESSAGE_DUMP_WITH_HEADER); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectRemoved", &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, BUS_MESSAGE_DUMP_WITH_HEADER); - - 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 deleted file mode 100644 index b6272efc30..0000000000 --- a/src/libsystemd/sd-bus/test-bus-server.c +++ /dev/null @@ -1,216 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#include "sd-bus.h" - -#include "bus-internal.h" -#include "bus-util.h" -#include "log.h" -#include "macro.h" -#include "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_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; - - r = sd_bus_process(bus, &m); - if (r < 0) { - log_error_errno(r, "Failed to process requests: %m"); - goto fail; - } - - if (r == 0) { - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) { - log_error_errno(r, "Failed to wait: %m"); - 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_errno(r, "Failed to allocate return: %m"); - goto fail; - } - - quit = true; - - } else if (sd_bus_message_is_method_call(m, NULL, NULL)) { - r = sd_bus_message_new_method_error( - m, - &reply, - &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method.")); - if (r < 0) { - log_error_errno(r, "Failed to allocate return: %m"); - goto fail; - } - } - - if (reply) { - r = sd_bus_send(bus, reply, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - 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_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; - _cleanup_(sd_bus_unrefp) 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, - &m, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.systemd.test", - "Exit"); - if (r < 0) - return log_error_errno(r, "Failed to allocate method call: %m"); - - 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 deleted file mode 100644 index 4f4fd093bf..0000000000 --- a/src/libsystemd/sd-bus/test-bus-signature.c +++ /dev/null @@ -1,164 +0,0 @@ -/*** - 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 . -***/ - -#include "bus-internal.h" -#include "bus-signature.h" -#include "log.h" -#include "string-util.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(path_complex_pattern("/foobar/waldo", "/foobar/")); - - assert_se(path_simple_pattern("/foo/", "/foo/bar/waldo")); - - assert_se(namespace_simple_pattern("", "")); - assert_se(namespace_simple_pattern("", ".foobar")); - 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(namespace_simple_pattern("foo.", "foo.bar.waldo")); - - 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 deleted file mode 100644 index 3380e8500a..0000000000 --- a/src/libsystemd/sd-bus/test-bus-zero-copy.c +++ /dev/null @@ -1,210 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-dump.h" -#include "bus-kernel.h" -#include "bus-message.h" -#include "fd-util.h" -#include "log.h" -#include "memfd-util.h" -#include "string-util.h" -#include "util.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; - const char *unique; - uint8_t *p; - sd_bus *a, *b; - int r, bus_ref; - sd_bus_message *m; - int f; - uint64_t sz; - uint32_t u32; - size_t i, l; - char *s; - _cleanup_close_ int sfd = -1; - - 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_get_unique_name(a, &unique); - assert_se(r >= 0); - - r = sd_bus_message_new_method_call(b, &m, unique, "/a/path", "an.inter.face", "AMethod"); - 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] = '>'; - - f = memfd_new_and_map(NULL, STRING_SIZE, (void**) &s); - assert_se(f >= 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 = memfd_get_size(f, &sz); - assert_se(r >= 0); - assert_se(sz == STRING_SIZE); - - r = sd_bus_message_append_string_memfd(m, f, 0, (uint64_t) -1); - assert_se(r >= 0); - - close(f); - - f = memfd_new_and_map(NULL, SECOND_ARRAY, (void**) &p); - assert_se(f >= 0); - - p[0] = '<'; - memset(p+1, 'P', SECOND_ARRAY-2); - p[SECOND_ARRAY-1] = '>'; - munmap(p, SECOND_ARRAY); - - r = memfd_get_size(f, &sz); - assert_se(r >= 0); - assert_se(sz == SECOND_ARRAY); - - r = sd_bus_message_append_array_memfd(m, 'y', f, 0, (uint64_t) -1); - assert_se(r >= 0); - - close(f); - - r = sd_bus_message_close_container(m); - assert_se(r >= 0); - - r = sd_bus_message_append(m, "u", 4711); - assert_se(r >= 0); - - assert_se((sfd = memfd_new_and_map(NULL, 6, (void**) &p)) >= 0); - memcpy(p, "abcd\0", 6); - munmap(p, 6); - assert_se(sd_bus_message_append_string_memfd(m, sfd, 1, 4) >= 0); - - r = bus_message_seal(m, 55, 99*USEC_PER_SEC); - assert_se(r >= 0); - - bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - 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, BUS_MESSAGE_DUMP_WITH_HEADER); - 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); - - r = sd_bus_message_read(m, "s", &s); - assert_se(r > 0); - assert_se(streq_ptr(s, "bcd")); - - sd_bus_message_unref(m); - - sd_bus_unref(a); - sd_bus_unref(b); - - return 0; -} diff --git a/src/libsystemd/sd-daemon/Makefile b/src/libsystemd/sd-daemon/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd/sd-daemon/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c deleted file mode 100644 index 4da9dbfd63..0000000000 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ /dev/null @@ -1,622 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "fs-util.h" -#include "parse-util.h" -#include "path-util.h" -#include "socket-util.h" -#include "strv.h" -#include "util.h" - -#define SNDBUF_SIZE (8*1024*1024) - -static void unsetenv_all(bool unset_environment) { - - if (!unset_environment) - return; - - unsetenv("LISTEN_PID"); - unsetenv("LISTEN_FDS"); - unsetenv("LISTEN_FDNAMES"); -} - -_public_ int sd_listen_fds(int unset_environment) { - const char *e; - int n, r, fd; - pid_t pid; - - e = getenv("LISTEN_PID"); - if (!e) { - r = 0; - goto finish; - } - - r = parse_pid(e, &pid); - if (r < 0) - goto finish; - - /* Is this for us? */ - if (getpid() != pid) { - r = 0; - goto finish; - } - - e = getenv("LISTEN_FDS"); - if (!e) { - r = 0; - goto finish; - } - - r = safe_atoi(e, &n); - if (r < 0) - goto finish; - - assert_cc(SD_LISTEN_FDS_START < INT_MAX); - if (n <= 0 || n > INT_MAX - SD_LISTEN_FDS_START) { - r = -EINVAL; - goto finish; - } - - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) { - r = fd_cloexec(fd, true); - if (r < 0) - goto finish; - } - - r = n; - -finish: - unsetenv_all(unset_environment); - return r; -} - -_public_ int sd_listen_fds_with_names(int unset_environment, char ***names) { - _cleanup_strv_free_ char **l = NULL; - bool have_names; - int n_names = 0, n_fds; - const char *e; - int r; - - if (!names) - return sd_listen_fds(unset_environment); - - e = getenv("LISTEN_FDNAMES"); - if (e) { - n_names = strv_split_extract(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (n_names < 0) { - unsetenv_all(unset_environment); - return n_names; - } - - have_names = true; - } else - have_names = false; - - n_fds = sd_listen_fds(unset_environment); - if (n_fds <= 0) - return n_fds; - - if (have_names) { - if (n_names != n_fds) - return -EINVAL; - } else { - r = strv_extend_n(&l, "unknown", n_fds); - if (r < 0) - return r; - } - - *names = l; - l = NULL; - - return n_fds; -} - -_public_ int sd_is_fifo(int fd, const char *path) { - struct stat st_fd; - - assert_return(fd >= 0, -EBADF); - - if (fstat(fd, &st_fd) < 0) - return -errno; - - if (!S_ISFIFO(st_fd.st_mode)) - return 0; - - if (path) { - struct stat st_path; - - if (stat(path, &st_path) < 0) { - - if (errno == ENOENT || errno == ENOTDIR) - return 0; - - return -errno; - } - - return - st_path.st_dev == st_fd.st_dev && - st_path.st_ino == st_fd.st_ino; - } - - return 1; -} - -_public_ int sd_is_special(int fd, const char *path) { - struct stat st_fd; - - assert_return(fd >= 0, -EBADF); - - if (fstat(fd, &st_fd) < 0) - return -errno; - - if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode)) - return 0; - - if (path) { - struct stat st_path; - - if (stat(path, &st_path) < 0) { - - if (errno == ENOENT || errno == ENOTDIR) - return 0; - - return -errno; - } - - if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode)) - return - st_path.st_dev == st_fd.st_dev && - st_path.st_ino == st_fd.st_ino; - else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode)) - return st_path.st_rdev == st_fd.st_rdev; - else - return 0; - } - - return 1; -} - -static int sd_is_socket_internal(int fd, int type, int listening) { - struct stat st_fd; - - assert_return(fd >= 0, -EBADF); - assert_return(type >= 0, -EINVAL); - - if (fstat(fd, &st_fd) < 0) - return -errno; - - if (!S_ISSOCK(st_fd.st_mode)) - return 0; - - if (type != 0) { - int other_type = 0; - socklen_t l = sizeof(other_type); - - if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0) - return -errno; - - if (l != sizeof(other_type)) - return -EINVAL; - - if (other_type != type) - return 0; - } - - if (listening >= 0) { - int accepting = 0; - socklen_t l = sizeof(accepting); - - if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) - return -errno; - - if (l != sizeof(accepting)) - return -EINVAL; - - if (!accepting != !listening) - return 0; - } - - return 1; -} - -_public_ int sd_is_socket(int fd, int family, int type, int listening) { - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(family >= 0, -EINVAL); - - r = sd_is_socket_internal(fd, type, listening); - if (r <= 0) - return r; - - if (family > 0) { - union sockaddr_union sockaddr = {}; - socklen_t l = sizeof(sockaddr); - - if (getsockname(fd, &sockaddr.sa, &l) < 0) - return -errno; - - if (l < sizeof(sa_family_t)) - return -EINVAL; - - return sockaddr.sa.sa_family == family; - } - - return 1; -} - -_public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { - union sockaddr_union sockaddr = {}; - socklen_t l = sizeof(sockaddr); - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL); - - r = sd_is_socket_internal(fd, type, listening); - if (r <= 0) - return r; - - if (getsockname(fd, &sockaddr.sa, &l) < 0) - return -errno; - - if (l < sizeof(sa_family_t)) - return -EINVAL; - - if (sockaddr.sa.sa_family != AF_INET && - sockaddr.sa.sa_family != AF_INET6) - return 0; - - if (family != 0) - if (sockaddr.sa.sa_family != family) - return 0; - - if (port > 0) { - if (sockaddr.sa.sa_family == AF_INET) { - if (l < sizeof(struct sockaddr_in)) - return -EINVAL; - - return htons(port) == sockaddr.in.sin_port; - } else { - if (l < sizeof(struct sockaddr_in6)) - return -EINVAL; - - return htons(port) == sockaddr.in6.sin6_port; - } - } - - return 1; -} - -_public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { - union sockaddr_union sockaddr = {}; - socklen_t l = sizeof(sockaddr); - int r; - - assert_return(fd >= 0, -EBADF); - - r = sd_is_socket_internal(fd, type, listening); - if (r <= 0) - return r; - - if (getsockname(fd, &sockaddr.sa, &l) < 0) - return -errno; - - if (l < sizeof(sa_family_t)) - return -EINVAL; - - if (sockaddr.sa.sa_family != AF_UNIX) - return 0; - - if (path) { - if (length == 0) - length = strlen(path); - - if (length == 0) - /* Unnamed socket */ - return l == offsetof(struct sockaddr_un, sun_path); - - if (path[0]) - /* Normal path socket */ - return - (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) && - memcmp(path, sockaddr.un.sun_path, length+1) == 0; - else - /* Abstract namespace socket */ - return - (l == offsetof(struct sockaddr_un, sun_path) + length) && - memcmp(path, sockaddr.un.sun_path, length) == 0; - } - - return 1; -} - -_public_ int sd_is_mq(int fd, const char *path) { - struct mq_attr attr; - - /* Check that the fd is valid */ - assert_return(fcntl(fd, F_GETFD) >= 0, -errno); - - if (mq_getattr(fd, &attr) < 0) { - if (errno == EBADF) - /* A non-mq fd (or an invalid one, but we ruled that out above) */ - return 0; - return -errno; - } - - if (path) { - char fpath[PATH_MAX]; - struct stat a, b; - - assert_return(path_is_absolute(path), -EINVAL); - - if (fstat(fd, &a) < 0) - return -errno; - - strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12); - fpath[sizeof(fpath)-1] = 0; - - if (stat(fpath, &b) < 0) - return -errno; - - if (a.st_dev != b.st_dev || - a.st_ino != b.st_ino) - return 0; - } - - return 1; -} - -_public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) { - union sockaddr_union sockaddr = { - .sa.sa_family = AF_UNIX, - }; - struct iovec iovec = { - .iov_base = (char*) state, - }; - struct msghdr msghdr = { - .msg_iov = &iovec, - .msg_iovlen = 1, - .msg_name = &sockaddr, - }; - _cleanup_close_ int fd = -1; - struct cmsghdr *cmsg = NULL; - const char *e; - bool have_pid; - int r; - - if (!state) { - r = -EINVAL; - goto finish; - } - - if (n_fds > 0 && !fds) { - r = -EINVAL; - goto finish; - } - - e = getenv("NOTIFY_SOCKET"); - if (!e) - return 0; - - /* Must be an abstract socket, or an absolute path */ - if ((e[0] != '@' && e[0] != '/') || e[1] == 0) { - r = -EINVAL; - goto finish; - } - - if (strlen(e) > sizeof(sockaddr.un.sun_path)) { - r = -EINVAL; - goto finish; - } - - fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (fd < 0) { - r = -errno; - goto finish; - } - - fd_inc_sndbuf(fd, SNDBUF_SIZE); - - iovec.iov_len = strlen(state); - - strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path)); - if (sockaddr.un.sun_path[0] == '@') - sockaddr.un.sun_path[0] = 0; - - msghdr.msg_namelen = SOCKADDR_UN_LEN(sockaddr.un); - - have_pid = pid != 0 && pid != getpid(); - - if (n_fds > 0 || have_pid) { - /* CMSG_SPACE(0) may return value different than zero, which results in miscalculated controllen. */ - msghdr.msg_controllen = - (n_fds > 0 ? CMSG_SPACE(sizeof(int) * n_fds) : 0) + - (have_pid ? CMSG_SPACE(sizeof(struct ucred)) : 0); - - msghdr.msg_control = alloca0(msghdr.msg_controllen); - - cmsg = CMSG_FIRSTHDR(&msghdr); - if (n_fds > 0) { - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds); - - memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds); - - if (have_pid) - assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg)); - } - - if (have_pid) { - struct ucred *ucred; - - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_CREDENTIALS; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - - ucred = (struct ucred*) CMSG_DATA(cmsg); - ucred->pid = pid; - ucred->uid = getuid(); - ucred->gid = getgid(); - } - } - - /* First try with fake ucred data, as requested */ - if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) { - r = 1; - goto finish; - } - - /* If that failed, try with our own ucred instead */ - if (have_pid) { - msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred)); - if (msghdr.msg_controllen == 0) - msghdr.msg_control = NULL; - - if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) { - r = 1; - goto finish; - } - } - - r = -errno; - -finish: - if (unset_environment) - unsetenv("NOTIFY_SOCKET"); - - return r; -} - -_public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) { - return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0); -} - -_public_ int sd_notify(int unset_environment, const char *state) { - return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0); -} - -_public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) { - _cleanup_free_ char *p = NULL; - int r; - - if (format) { - va_list ap; - - va_start(ap, format); - r = vasprintf(&p, format, ap); - va_end(ap); - - if (r < 0 || !p) - return -ENOMEM; - } - - return sd_pid_notify(pid, unset_environment, p); -} - -_public_ int sd_notifyf(int unset_environment, const char *format, ...) { - _cleanup_free_ char *p = NULL; - int r; - - if (format) { - va_list ap; - - va_start(ap, format); - r = vasprintf(&p, format, ap); - va_end(ap); - - if (r < 0 || !p) - return -ENOMEM; - } - - return sd_pid_notify(0, unset_environment, p); -} - -_public_ int sd_booted(void) { - /* We test whether the runtime unit file directory has been - * created. This takes place in mount-setup.c, so is - * guaranteed to happen very early during boot. */ - - return laccess("/run/systemd/system/", F_OK) >= 0; -} - -_public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) { - const char *s, *p = ""; /* p is set to dummy value to do unsetting */ - uint64_t u; - int r = 0; - - s = getenv("WATCHDOG_USEC"); - if (!s) - goto finish; - - r = safe_atou64(s, &u); - if (r < 0) - goto finish; - if (u <= 0 || u >= USEC_INFINITY) { - r = -EINVAL; - goto finish; - } - - p = getenv("WATCHDOG_PID"); - if (p) { - pid_t pid; - - r = parse_pid(p, &pid); - if (r < 0) - goto finish; - - /* Is this for us? */ - if (getpid() != pid) { - r = 0; - goto finish; - } - } - - if (usec) - *usec = u; - - r = 1; - -finish: - if (unset_environment && s) - unsetenv("WATCHDOG_USEC"); - if (unset_environment && p) - unsetenv("WATCHDOG_PID"); - - return r; -} diff --git a/src/libsystemd/sd-device/Makefile b/src/libsystemd/sd-device/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd/sd-device/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-device/device-enumerator-private.h b/src/libsystemd/sd-device/device-enumerator-private.h deleted file mode 100644 index eb06f9542d..0000000000 --- a/src/libsystemd/sd-device/device-enumerator-private.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Tom Gundersen - - 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 . -***/ - -#include "sd-device.h" - -int device_enumerator_scan_devices(sd_device_enumerator *enumeartor); -int device_enumerator_scan_subsystems(sd_device_enumerator *enumeartor); -int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device); -int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator); -sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator); -sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator); - -#define FOREACH_DEVICE_AND_SUBSYSTEM(enumerator, device) \ - for (device = device_enumerator_get_first(enumerator); \ - device; \ - device = device_enumerator_get_next(enumerator)) diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c deleted file mode 100644 index 4a7a8b1f9e..0000000000 --- a/src/libsystemd/sd-device/device-enumerator.c +++ /dev/null @@ -1,986 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - Copyright 2014-2015 Tom Gundersen - - 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 . -***/ - -#include "sd-device.h" - -#include "alloc-util.h" -#include "device-enumerator-private.h" -#include "device-util.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "prioq.h" -#include "set.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -#define DEVICE_ENUMERATE_MAX_DEPTH 256 - -typedef enum DeviceEnumerationType { - DEVICE_ENUMERATION_TYPE_DEVICES, - DEVICE_ENUMERATION_TYPE_SUBSYSTEMS, - _DEVICE_ENUMERATION_TYPE_MAX, - _DEVICE_ENUMERATION_TYPE_INVALID = -1, -} DeviceEnumerationType; - -struct sd_device_enumerator { - unsigned n_ref; - - DeviceEnumerationType type; - Prioq *devices; - bool scan_uptodate; - - Set *match_subsystem; - Set *nomatch_subsystem; - Hashmap *match_sysattr; - Hashmap *nomatch_sysattr; - Hashmap *match_property; - Set *match_sysname; - Set *match_tag; - sd_device *match_parent; - bool match_allow_uninitialized; -}; - -_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { - _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerator = NULL; - - assert(ret); - - enumerator = new0(sd_device_enumerator, 1); - if (!enumerator) - return -ENOMEM; - - enumerator->n_ref = 1; - enumerator->type = _DEVICE_ENUMERATION_TYPE_INVALID; - - *ret = enumerator; - enumerator = NULL; - - return 0; -} - -_public_ sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator) { - assert_return(enumerator, NULL); - - assert_se((++ enumerator->n_ref) >= 2); - - return enumerator; -} - -_public_ sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator) { - if (enumerator && (-- enumerator->n_ref) == 0) { - sd_device *device; - - while ((device = prioq_pop(enumerator->devices))) - sd_device_unref(device); - - prioq_free(enumerator->devices); - - set_free_free(enumerator->match_subsystem); - set_free_free(enumerator->nomatch_subsystem); - hashmap_free_free_free(enumerator->match_sysattr); - hashmap_free_free_free(enumerator->nomatch_sysattr); - hashmap_free_free_free(enumerator->match_property); - set_free_free(enumerator->match_sysname); - set_free_free(enumerator->match_tag); - sd_device_unref(enumerator->match_parent); - - free(enumerator); - } - - return NULL; -} - -_public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) { - Set **set; - int r; - - assert_return(enumerator, -EINVAL); - assert_return(subsystem, -EINVAL); - - if (match) - set = &enumerator->match_subsystem; - else - set = &enumerator->nomatch_subsystem; - - r = set_ensure_allocated(set, NULL); - if (r < 0) - return r; - - r = set_put_strdup(*set, subsystem); - if (r < 0) - return r; - - enumerator->scan_uptodate = false; - - return 0; -} - -_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *_sysattr, const char *_value, int match) { - _cleanup_free_ char *sysattr = NULL, *value = NULL; - Hashmap **hashmap; - int r; - - assert_return(enumerator, -EINVAL); - assert_return(_sysattr, -EINVAL); - - if (match) - hashmap = &enumerator->match_sysattr; - else - hashmap = &enumerator->nomatch_sysattr; - - r = hashmap_ensure_allocated(hashmap, NULL); - if (r < 0) - return r; - - sysattr = strdup(_sysattr); - if (!sysattr) - return -ENOMEM; - - if (_value) { - value = strdup(_value); - if (!value) - return -ENOMEM; - } - - r = hashmap_put(*hashmap, sysattr, value); - if (r < 0) - return r; - - sysattr = NULL; - value = NULL; - - enumerator->scan_uptodate = false; - - return 0; -} - -_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *_property, const char *_value) { - _cleanup_free_ char *property = NULL, *value = NULL; - int r; - - assert_return(enumerator, -EINVAL); - assert_return(_property, -EINVAL); - - r = hashmap_ensure_allocated(&enumerator->match_property, NULL); - if (r < 0) - return r; - - property = strdup(_property); - if (!property) - return -ENOMEM; - - if (_value) { - value = strdup(_value); - if (!value) - return -ENOMEM; - } - - r = hashmap_put(enumerator->match_property, property, value); - if (r < 0) - return r; - - property = NULL; - value = NULL; - - enumerator->scan_uptodate = false; - - return 0; -} - -_public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname) { - int r; - - assert_return(enumerator, -EINVAL); - assert_return(sysname, -EINVAL); - - r = set_ensure_allocated(&enumerator->match_sysname, NULL); - if (r < 0) - return r; - - r = set_put_strdup(enumerator->match_sysname, sysname); - if (r < 0) - return r; - - enumerator->scan_uptodate = false; - - return 0; -} - -_public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag) { - int r; - - assert_return(enumerator, -EINVAL); - assert_return(tag, -EINVAL); - - r = set_ensure_allocated(&enumerator->match_tag, NULL); - if (r < 0) - return r; - - r = set_put_strdup(enumerator->match_tag, tag); - if (r < 0) - return r; - - enumerator->scan_uptodate = false; - - return 0; -} - -_public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) { - assert_return(enumerator, -EINVAL); - assert_return(parent, -EINVAL); - - sd_device_unref(enumerator->match_parent); - enumerator->match_parent = sd_device_ref(parent); - - enumerator->scan_uptodate = false; - - return 0; -} - -_public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) { - assert_return(enumerator, -EINVAL); - - enumerator->match_allow_uninitialized = true; - - enumerator->scan_uptodate = false; - - return 0; -} - -int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) { - assert_return(enumerator, -EINVAL); - - enumerator->match_allow_uninitialized = false; - - enumerator->scan_uptodate = false; - - return 0; -} - -static int device_compare(const void *_a, const void *_b) { - sd_device *a = (sd_device *)_a, *b = (sd_device *)_b; - const char *devpath_a, *devpath_b, *sound_a; - bool delay_a, delay_b; - - assert_se(sd_device_get_devpath(a, &devpath_a) >= 0); - assert_se(sd_device_get_devpath(b, &devpath_b) >= 0); - - sound_a = strstr(devpath_a, "/sound/card"); - if (sound_a) { - /* For sound cards the control device must be enumerated last to - * make sure it's the final device node that gets ACLs applied. - * Applications rely on this fact and use ACL changes on the - * control node as an indicator that the ACL change of the - * entire sound card completed. The kernel makes this guarantee - * when creating those devices, and hence we should too when - * enumerating them. */ - sound_a += strlen("/sound/card"); - sound_a = strchr(sound_a, '/'); - - if (sound_a) { - unsigned prefix_len; - - prefix_len = sound_a - devpath_a; - - if (strncmp(devpath_a, devpath_b, prefix_len) == 0) { - const char *sound_b; - - sound_b = devpath_b + prefix_len; - - if (startswith(sound_a, "/controlC") && - !startswith(sound_b, "/contolC")) - return 1; - - if (!startswith(sound_a, "/controlC") && - startswith(sound_b, "/controlC")) - return -1; - } - } - } - - /* md and dm devices are enumerated after all other devices */ - delay_a = strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-"); - delay_b = strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-"); - if (delay_a != delay_b) - return delay_a - delay_b; - - return strcmp(devpath_a, devpath_b); -} - -int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) { - int r; - - assert_return(enumerator, -EINVAL); - assert_return(device, -EINVAL); - - r = prioq_ensure_allocated(&enumerator->devices, device_compare); - if (r < 0) - return r; - - r = prioq_put(enumerator->devices, device, NULL); - if (r < 0) - return r; - - sd_device_ref(device); - - return 0; -} - -static bool match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) { - const char *value; - int r; - - assert(device); - assert(sysattr); - - r = sd_device_get_sysattr_value(device, sysattr, &value); - if (r < 0) - return false; - - if (!match_value) - return true; - - if (fnmatch(match_value, value, 0) == 0) - return true; - - return false; -} - -static bool match_sysattr(sd_device_enumerator *enumerator, sd_device *device) { - const char *sysattr; - const char *value; - Iterator i; - - assert(enumerator); - assert(device); - - HASHMAP_FOREACH_KEY(value, sysattr, enumerator->nomatch_sysattr, i) - if (match_sysattr_value(device, sysattr, value)) - return false; - - HASHMAP_FOREACH_KEY(value, sysattr, enumerator->match_sysattr, i) - if (!match_sysattr_value(device, sysattr, value)) - return false; - - return true; -} - -static bool match_property(sd_device_enumerator *enumerator, sd_device *device) { - const char *property; - const char *value; - Iterator i; - - assert(enumerator); - assert(device); - - if (hashmap_isempty(enumerator->match_property)) - return true; - - HASHMAP_FOREACH_KEY(value, property, enumerator->match_property, i) { - const char *property_dev, *value_dev; - - FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) { - if (fnmatch(property, property_dev, 0) != 0) - continue; - - if (!value && !value_dev) - return true; - - if (!value || !value_dev) - continue; - - if (fnmatch(value, value_dev, 0) == 0) - return true; - } - } - - return false; -} - -static bool match_tag(sd_device_enumerator *enumerator, sd_device *device) { - const char *tag; - Iterator i; - - assert(enumerator); - assert(device); - - SET_FOREACH(tag, enumerator->match_tag, i) - if (!sd_device_has_tag(device, tag)) - return false; - - return true; -} - -static bool match_parent(sd_device_enumerator *enumerator, sd_device *device) { - const char *devpath, *devpath_dev; - int r; - - assert(enumerator); - assert(device); - - if (!enumerator->match_parent) - return true; - - r = sd_device_get_devpath(enumerator->match_parent, &devpath); - assert(r >= 0); - - r = sd_device_get_devpath(device, &devpath_dev); - assert(r >= 0); - - return startswith(devpath_dev, devpath); -} - -static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) { - const char *sysname_match; - Iterator i; - - assert(enumerator); - assert(sysname); - - if (set_isempty(enumerator->match_sysname)) - return true; - - SET_FOREACH(sysname_match, enumerator->match_sysname, i) - if (fnmatch(sysname_match, sysname, 0) == 0) - return true; - - return false; -} - -static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) { - _cleanup_closedir_ DIR *dir = NULL; - char *path; - struct dirent *dent; - int r = 0; - - assert(enumerator); - assert(basedir); - - path = strjoina("/sys/", basedir, "/"); - - if (subdir1) - path = strjoina(path, subdir1, "/"); - - if (subdir2) - path = strjoina(path, subdir2, "/"); - - dir = opendir(path); - if (!dir) - return -errno; - - FOREACH_DIRENT_ALL(dent, dir, return -errno) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - char syspath[strlen(path) + 1 + strlen(dent->d_name) + 1]; - dev_t devnum; - int ifindex, initialized, k; - - if (dent->d_name[0] == '.') - continue; - - if (!match_sysname(enumerator, dent->d_name)) - continue; - - (void)sprintf(syspath, "%s%s", path, dent->d_name); - - k = sd_device_new_from_syspath(&device, syspath); - if (k < 0) { - if (k != -ENODEV) - /* this is necessarily racey, so ignore missing devices */ - r = k; - - continue; - } - - k = sd_device_get_devnum(device, &devnum); - if (k < 0) { - r = k; - continue; - } - - k = sd_device_get_ifindex(device, &ifindex); - if (k < 0) { - r = k; - continue; - } - - k = sd_device_get_is_initialized(device, &initialized); - if (k < 0) { - r = k; - continue; - } - - /* - * All devices with a device node or network interfaces - * possibly need udev to adjust the device node permission - * or context, or rename the interface before it can be - * reliably used from other processes. - * - * For now, we can only check these types of devices, we - * might not store a database, and have no way to find out - * for all other types of devices. - */ - if (!enumerator->match_allow_uninitialized && - !initialized && - (major(devnum) > 0 || ifindex > 0)) - continue; - - if (!match_parent(enumerator, device)) - continue; - - if (!match_tag(enumerator, device)) - continue; - - if (!match_property(enumerator, device)) - continue; - - if (!match_sysattr(enumerator, device)) - continue; - - k = device_enumerator_add_device(enumerator, device); - if (k < 0) - r = k; - } - - return r; -} - -static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsystem) { - const char *subsystem_match; - Iterator i; - - assert(enumerator); - - if (!subsystem) - return false; - - SET_FOREACH(subsystem_match, enumerator->nomatch_subsystem, i) - if (fnmatch(subsystem_match, subsystem, 0) == 0) - return false; - - if (set_isempty(enumerator->match_subsystem)) - return true; - - SET_FOREACH(subsystem_match, enumerator->match_subsystem, i) - if (fnmatch(subsystem_match, subsystem, 0) == 0) - return true; - - return false; -} - -static int enumerator_scan_dir(sd_device_enumerator *enumerator, const char *basedir, const char *subdir, const char *subsystem) { - _cleanup_closedir_ DIR *dir = NULL; - char *path; - struct dirent *dent; - int r = 0; - - path = strjoina("/sys/", basedir); - - dir = opendir(path); - if (!dir) - return -errno; - - log_debug(" device-enumerator: scanning %s", path); - - FOREACH_DIRENT_ALL(dent, dir, return -errno) { - int k; - - if (dent->d_name[0] == '.') - continue; - - if (!match_subsystem(enumerator, subsystem ? : dent->d_name)) - continue; - - k = enumerator_scan_dir_and_add_devices(enumerator, basedir, dent->d_name, subdir); - if (k < 0) - r = k; - } - - return r; -} - -static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const char *tag) { - _cleanup_closedir_ DIR *dir = NULL; - char *path; - struct dirent *dent; - int r = 0; - - assert(enumerator); - assert(tag); - - path = strjoina("/run/udev/tags/", tag); - - dir = opendir(path); - if (!dir) { - if (errno == ENOENT) - return 0; - else { - log_error("sd-device-enumerator: could not open tags directory %s: %m", path); - return -errno; - } - } - - /* TODO: filter away subsystems? */ - - FOREACH_DIRENT_ALL(dent, dir, return -errno) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - const char *subsystem, *sysname; - int k; - - if (dent->d_name[0] == '.') - continue; - - k = sd_device_new_from_device_id(&device, dent->d_name); - if (k < 0) { - if (k != -ENODEV) - /* this is necessarily racy, so ignore missing devices */ - r = k; - - continue; - } - - k = sd_device_get_subsystem(device, &subsystem); - if (k < 0) { - r = k; - continue; - } - - if (!match_subsystem(enumerator, subsystem)) - continue; - - k = sd_device_get_sysname(device, &sysname); - if (k < 0) { - r = k; - continue; - } - - if (!match_sysname(enumerator, sysname)) - continue; - - if (!match_parent(enumerator, device)) - continue; - - if (!match_property(enumerator, device)) - continue; - - if (!match_sysattr(enumerator, device)) - continue; - - k = device_enumerator_add_device(enumerator, device); - if (k < 0) { - r = k; - continue; - } - } - - return r; -} - -static int enumerator_scan_devices_tags(sd_device_enumerator *enumerator) { - const char *tag; - Iterator i; - int r; - - assert(enumerator); - - SET_FOREACH(tag, enumerator->match_tag, i) { - r = enumerator_scan_devices_tag(enumerator, tag); - if (r < 0) - return r; - } - - return 0; -} - -static int parent_add_child(sd_device_enumerator *enumerator, const char *path) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - const char *subsystem, *sysname; - int r; - - r = sd_device_new_from_syspath(&device, path); - if (r == -ENODEV) - /* this is necessarily racy, so ignore missing devices */ - return 0; - else if (r < 0) - return r; - - r = sd_device_get_subsystem(device, &subsystem); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - if (!match_subsystem(enumerator, subsystem)) - return 0; - - r = sd_device_get_sysname(device, &sysname); - if (r < 0) - return r; - - if (!match_sysname(enumerator, sysname)) - return 0; - - if (!match_property(enumerator, device)) - return 0; - - if (!match_sysattr(enumerator, device)) - return 0; - - r = device_enumerator_add_device(enumerator, device); - if (r < 0) - return r; - - return 1; -} - -static int parent_crawl_children(sd_device_enumerator *enumerator, const char *path, unsigned maxdepth) { - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *dent; - int r = 0; - - dir = opendir(path); - if (!dir) { - log_debug("sd-device-enumerate: could not open parent directory %s: %m", path); - return -errno; - } - - FOREACH_DIRENT_ALL(dent, dir, return -errno) { - _cleanup_free_ char *child = NULL; - int k; - - if (dent->d_name[0] == '.') - continue; - - if (dent->d_type != DT_DIR) - continue; - - child = strjoin(path, "/", dent->d_name, NULL); - if (!child) - return -ENOMEM; - - k = parent_add_child(enumerator, child); - if (k < 0) - r = k; - - if (maxdepth > 0) - parent_crawl_children(enumerator, child, maxdepth - 1); - else - log_debug("device-enumerate: max depth reached, %s: ignoring devices", child); - } - - return r; -} - -static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) { - const char *path; - int r = 0, k; - - r = sd_device_get_syspath(enumerator->match_parent, &path); - if (r < 0) - return r; - - k = parent_add_child(enumerator, path); - if (k < 0) - r = k; - - k = parent_crawl_children(enumerator, path, DEVICE_ENUMERATE_MAX_DEPTH); - if (k < 0) - r = k; - - return r; -} - -static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) { - int r = 0; - - log_debug("device-enumerator: scan all dirs"); - - if (access("/sys/subsystem", F_OK) >= 0) { - /* we have /subsystem/, forget all the old stuff */ - r = enumerator_scan_dir(enumerator, "subsystem", "devices", NULL); - if (r < 0) - return log_debug_errno(r, "device-enumerator: failed to scan /sys/subsystem: %m"); - } else { - int k; - - k = enumerator_scan_dir(enumerator, "bus", "devices", NULL); - if (k < 0) { - log_debug_errno(k, "device-enumerator: failed to scan /sys/bus: %m"); - r = k; - } - - k = enumerator_scan_dir(enumerator, "class", NULL, NULL); - if (k < 0) { - log_debug_errno(k, "device-enumerator: failed to scan /sys/class: %m"); - r = k; - } - } - - return r; -} - -int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { - sd_device *device; - int r; - - assert(enumerator); - - if (enumerator->scan_uptodate && - enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES) - return 0; - - while ((device = prioq_pop(enumerator->devices))) - sd_device_unref(device); - - if (!set_isempty(enumerator->match_tag)) { - r = enumerator_scan_devices_tags(enumerator); - if (r < 0) - return r; - } else if (enumerator->match_parent) { - r = enumerator_scan_devices_children(enumerator); - if (r < 0) - return r; - } else { - r = enumerator_scan_devices_all(enumerator); - if (r < 0) - return r; - } - - enumerator->scan_uptodate = true; - - return 0; -} - -_public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) { - int r; - - assert_return(enumerator, NULL); - - r = device_enumerator_scan_devices(enumerator); - if (r < 0) - return NULL; - - enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES; - - return prioq_peek(enumerator->devices); -} - -_public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator) { - assert_return(enumerator, NULL); - - if (!enumerator->scan_uptodate || - enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES) - return NULL; - - sd_device_unref(prioq_pop(enumerator->devices)); - - return prioq_peek(enumerator->devices); -} - -int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { - sd_device *device; - const char *subsysdir; - int r = 0, k; - - assert(enumerator); - - if (enumerator->scan_uptodate && - enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS) - return 0; - - while ((device = prioq_pop(enumerator->devices))) - sd_device_unref(device); - - /* modules */ - if (match_subsystem(enumerator, "module")) { - k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL); - if (k < 0) { - log_debug_errno(k, "device-enumerator: failed to scan modules: %m"); - r = k; - } - } - - if (access("/sys/subsystem", F_OK) >= 0) - subsysdir = "subsystem"; - else - subsysdir = "bus"; - - /* subsystems (only buses support coldplug) */ - if (match_subsystem(enumerator, "subsystem")) { - k = enumerator_scan_dir_and_add_devices(enumerator, subsysdir, NULL, NULL); - if (k < 0) { - log_debug_errno(k, "device-enumerator: failed to scan subsystems: %m"); - r = k; - } - } - - /* subsystem drivers */ - if (match_subsystem(enumerator, "drivers")) { - k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers"); - if (k < 0) { - log_debug_errno(k, "device-enumerator: failed to scan drivers: %m"); - r = k; - } - } - - enumerator->scan_uptodate = true; - - return r; -} - -_public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator) { - int r; - - assert_return(enumerator, NULL); - - r = device_enumerator_scan_subsystems(enumerator); - if (r < 0) - return NULL; - - enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS; - - return prioq_peek(enumerator->devices); -} - -_public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator) { - assert_return(enumerator, NULL); - - if (enumerator->scan_uptodate || - enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS) - return NULL; - - sd_device_unref(prioq_pop(enumerator->devices)); - - return prioq_peek(enumerator->devices); -} - -sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) { - assert_return(enumerator, NULL); - - return prioq_peek(enumerator->devices); -} - -sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) { - assert_return(enumerator, NULL); - - sd_device_unref(prioq_pop(enumerator->devices)); - - return prioq_peek(enumerator->devices); -} diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h deleted file mode 100644 index ab222e27de..0000000000 --- a/src/libsystemd/sd-device/device-internal.h +++ /dev/null @@ -1,126 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - Copyright 2014 Tom Gundersen - - 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 . -***/ - -#include "hashmap.h" -#include "set.h" - -struct sd_device { - uint64_t n_ref; - - sd_device *parent; - bool parent_set; /* no need to try to reload parent */ - - OrderedHashmap *properties; - Iterator properties_iterator; - uint64_t properties_generation; /* changes whenever the properties are changed */ - uint64_t properties_iterator_generation; /* generation when iteration was started */ - - /* the subset of the properties that should be written to the db*/ - OrderedHashmap *properties_db; - - Hashmap *sysattr_values; /* cached sysattr values */ - - Set *sysattrs; /* names of sysattrs */ - Iterator sysattrs_iterator; - bool sysattrs_read; /* don't try to re-read sysattrs once read */ - - Set *tags; - Iterator tags_iterator; - uint64_t tags_generation; /* changes whenever the tags are changed */ - uint64_t tags_iterator_generation; /* generation when iteration was started */ - bool property_tags_outdated; /* need to update TAGS= property */ - - Set *devlinks; - Iterator devlinks_iterator; - uint64_t devlinks_generation; /* changes whenever the devlinks are changed */ - uint64_t devlinks_iterator_generation; /* generation when iteration was started */ - bool property_devlinks_outdated; /* need to update DEVLINKS= property */ - int devlink_priority; - - char **properties_strv; /* the properties hashmap as a strv */ - uint8_t *properties_nulstr; /* the same as a nulstr */ - size_t properties_nulstr_len; - bool properties_buf_outdated; /* need to reread hashmap */ - - int watch_handle; - - char *syspath; - const char *devpath; - const char *sysnum; - char *sysname; - bool sysname_set; /* don't reread sysname */ - - char *devtype; - int ifindex; - char *devname; - dev_t devnum; - - char *subsystem; - bool subsystem_set; /* don't reread subsystem */ - char *driver; - bool driver_set; /* don't reread driver */ - - char *id_filename; - - bool is_initialized; - uint64_t usec_initialized; - - mode_t devmode; - uid_t devuid; - gid_t devgid; - - bool uevent_loaded; /* don't reread uevent */ - bool db_loaded; /* don't reread db */ - - bool sealed; /* don't read more information from uevent/db */ - bool db_persist; /* don't clean up the db when switching from initrd to real root */ -}; - -typedef enum DeviceAction { - DEVICE_ACTION_ADD, - DEVICE_ACTION_REMOVE, - DEVICE_ACTION_CHANGE, - DEVICE_ACTION_MOVE, - DEVICE_ACTION_ONLINE, - DEVICE_ACTION_OFFLINE, - _DEVICE_ACTION_MAX, - _DEVICE_ACTION_INVALID = -1, -} DeviceAction; - -int device_new_aux(sd_device **ret); -int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db); -int device_add_property_internal(sd_device *device, const char *key, const char *value); -int device_read_uevent_file(sd_device *device); -int device_read_db_aux(sd_device *device, bool force); - -int device_set_syspath(sd_device *device, const char *_syspath, bool verify); -int device_set_ifindex(sd_device *device, const char *ifindex); -int device_set_devmode(sd_device *device, const char *devmode); -int device_set_devname(sd_device *device, const char *_devname); -int device_set_devtype(sd_device *device, const char *_devtype); -int device_set_devnum(sd_device *device, const char *major, const char *minor); -int device_set_subsystem(sd_device *device, const char *_subsystem); -int device_set_driver(sd_device *device, const char *_driver); -int device_set_usec_initialized(sd_device *device, const char *initialized); - -DeviceAction device_action_from_string(const char *s) _pure_; -const char *device_action_to_string(DeviceAction a) _const_; diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c deleted file mode 100644 index 9082d377f4..0000000000 --- a/src/libsystemd/sd-device/device-private.c +++ /dev/null @@ -1,1119 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - Copyright 2014 Tom Gundersen - - 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 . -***/ - -#include -#include -#include - -#include "sd-device.h" - -#include "alloc-util.h" -#include "device-internal.h" -#include "device-private.h" -#include "device-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hashmap.h" -#include "macro.h" -#include "mkdir.h" -#include "parse-util.h" -#include "path-util.h" -#include "refcnt.h" -#include "set.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "strxcpyx.h" -#include "user-util.h" -#include "util.h" - -int device_add_property(sd_device *device, const char *key, const char *value) { - int r; - - assert(device); - assert(key); - - r = device_add_property_aux(device, key, value, false); - if (r < 0) - return r; - - if (key[0] != '.') { - r = device_add_property_aux(device, key, value, true); - if (r < 0) - return r; - } - - return 0; -} - -static int device_add_property_internal_from_string(sd_device *device, const char *str) { - _cleanup_free_ char *key = NULL; - char *value; - - assert(device); - assert(str); - - key = strdup(str); - if (!key) - return -ENOMEM; - - value = strchr(key, '='); - if (!value) - return -EINVAL; - - *value = '\0'; - - if (isempty(++value)) - value = NULL; - - return device_add_property_internal(device, key, value); -} - -static int handle_db_line(sd_device *device, char key, const char *value) { - char *path; - int r; - - assert(device); - assert(value); - - switch (key) { - case 'S': - path = strjoina("/dev/", value); - r = device_add_devlink(device, path); - if (r < 0) - return r; - - break; - case 'L': - r = safe_atoi(value, &device->devlink_priority); - if (r < 0) - return r; - - break; - case 'E': - r = device_add_property_internal_from_string(device, value); - if (r < 0) - return r; - - break; - case 'G': - r = device_add_tag(device, value); - if (r < 0) - return r; - - break; - case 'W': - r = safe_atoi(value, &device->watch_handle); - if (r < 0) - return r; - - break; - case 'I': - r = device_set_usec_initialized(device, value); - if (r < 0) - return r; - - break; - default: - log_debug("device db: unknown key '%c'", key); - } - - return 0; -} - -void device_set_devlink_priority(sd_device *device, int priority) { - assert(device); - - device->devlink_priority = priority; -} - -void device_set_is_initialized(sd_device *device) { - assert(device); - - device->is_initialized = true; -} - -int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) { - char num[DECIMAL_STR_MAX(usec_t)]; - usec_t usec_initialized; - int r; - - assert(device); - - if (device_old && device_old->usec_initialized > 0) - usec_initialized = device_old->usec_initialized; - else - usec_initialized = now(CLOCK_MONOTONIC); - - r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized); - if (r < 0) - return -errno; - - r = device_set_usec_initialized(device, num); - if (r < 0) - return r; - - return 0; -} - -static int device_read_db(sd_device *device) { - _cleanup_free_ char *db = NULL; - char *path; - const char *id, *value; - char key; - size_t db_len; - unsigned i; - int r; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - INVALID_LINE, - } state = PRE_KEY; - - assert(device); - - if (device->db_loaded || device->sealed) - return 0; - - r = device_get_id_filename(device, &id); - if (r < 0) - return r; - - path = strjoina("/run/udev/data/", id); - - r = read_full_file(path, &db, &db_len); - if (r < 0) { - if (r == -ENOENT) - return 0; - else - return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path); - } - - /* devices with a database entry are initialized */ - device_set_is_initialized(device); - - for (i = 0; i < db_len; i++) { - switch (state) { - case PRE_KEY: - if (!strchr(NEWLINE, db[i])) { - key = db[i]; - - state = KEY; - } - - break; - case KEY: - if (db[i] != ':') { - log_debug("sd-device: ignoring invalid db entry with key '%c'", key); - - state = INVALID_LINE; - } else { - db[i] = '\0'; - - state = PRE_VALUE; - } - - break; - case PRE_VALUE: - value = &db[i]; - - state = VALUE; - - break; - case INVALID_LINE: - if (strchr(NEWLINE, db[i])) - state = PRE_KEY; - - break; - case VALUE: - if (strchr(NEWLINE, db[i])) { - db[i] = '\0'; - r = handle_db_line(device, key, value); - if (r < 0) - log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value); - - state = PRE_KEY; - } - - break; - default: - assert_not_reached("invalid state when parsing db"); - } - } - - device->db_loaded = true; - - return 0; -} - -uint64_t device_get_properties_generation(sd_device *device) { - assert(device); - - return device->properties_generation; -} - -uint64_t device_get_tags_generation(sd_device *device) { - assert(device); - - return device->tags_generation; -} - -uint64_t device_get_devlinks_generation(sd_device *device) { - assert(device); - - return device->devlinks_generation; -} - -int device_get_devnode_mode(sd_device *device, mode_t *mode) { - int r; - - assert(device); - assert(mode); - - r = device_read_db(device); - if (r < 0) - return r; - - *mode = device->devmode; - - return 0; -} - -int device_get_devnode_uid(sd_device *device, uid_t *uid) { - int r; - - assert(device); - assert(uid); - - r = device_read_db(device); - if (r < 0) - return r; - - *uid = device->devuid; - - return 0; -} - -static int device_set_devuid(sd_device *device, const char *uid) { - unsigned u; - int r; - - assert(device); - assert(uid); - - r = safe_atou(uid, &u); - if (r < 0) - return r; - - r = device_add_property_internal(device, "DEVUID", uid); - if (r < 0) - return r; - - device->devuid = u; - - return 0; -} - -int device_get_devnode_gid(sd_device *device, gid_t *gid) { - int r; - - assert(device); - assert(gid); - - r = device_read_db(device); - if (r < 0) - return r; - - *gid = device->devgid; - - return 0; -} - -static int device_set_devgid(sd_device *device, const char *gid) { - unsigned g; - int r; - - assert(device); - assert(gid); - - r = safe_atou(gid, &g); - if (r < 0) - return r; - - r = device_add_property_internal(device, "DEVGID", gid); - if (r < 0) - return r; - - device->devgid = g; - - return 0; -} - -static int device_amend(sd_device *device, const char *key, const char *value) { - int r; - - assert(device); - assert(key); - assert(value); - - if (streq(key, "DEVPATH")) { - char *path; - - path = strjoina("/sys", value); - - /* the caller must verify or trust this data (e.g., if it comes from the kernel) */ - r = device_set_syspath(device, path, false); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set syspath to '%s': %m", path); - } else if (streq(key, "SUBSYSTEM")) { - r = device_set_subsystem(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set subsystem to '%s': %m", value); - } else if (streq(key, "DEVTYPE")) { - r = device_set_devtype(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devtype to '%s': %m", value); - } else if (streq(key, "DEVNAME")) { - r = device_set_devname(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devname to '%s': %m", value); - } else if (streq(key, "USEC_INITIALIZED")) { - r = device_set_usec_initialized(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set usec-initialized to '%s': %m", value); - } else if (streq(key, "DRIVER")) { - r = device_set_driver(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set driver to '%s': %m", value); - } else if (streq(key, "IFINDEX")) { - r = device_set_ifindex(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set ifindex to '%s': %m", value); - } else if (streq(key, "DEVMODE")) { - r = device_set_devmode(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devmode to '%s': %m", value); - } else if (streq(key, "DEVUID")) { - r = device_set_devuid(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devuid to '%s': %m", value); - } else if (streq(key, "DEVGID")) { - r = device_set_devgid(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devgid to '%s': %m", value); - } else if (streq(key, "DEVLINKS")) { - const char *word, *state; - size_t l; - - FOREACH_WORD(word, l, value, state) { - char devlink[l + 1]; - - strncpy(devlink, word, l); - devlink[l] = '\0'; - - r = device_add_devlink(device, devlink); - if (r < 0) - return log_debug_errno(r, "sd-device: could not add devlink '%s': %m", devlink); - } - } else if (streq(key, "TAGS")) { - const char *word, *state; - size_t l; - - FOREACH_WORD_SEPARATOR(word, l, value, ":", state) { - char tag[l + 1]; - - (void)strncpy(tag, word, l); - tag[l] = '\0'; - - r = device_add_tag(device, tag); - if (r < 0) - return log_debug_errno(r, "sd-device: could not add tag '%s': %m", tag); - } - } else { - r = device_add_property_internal(device, key, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not add property '%s=%s': %m", key, value); - } - - return 0; -} - -static const char* const device_action_table[_DEVICE_ACTION_MAX] = { - [DEVICE_ACTION_ADD] = "add", - [DEVICE_ACTION_REMOVE] = "remove", - [DEVICE_ACTION_CHANGE] = "change", - [DEVICE_ACTION_MOVE] = "move", - [DEVICE_ACTION_ONLINE] = "online", - [DEVICE_ACTION_OFFLINE] = "offline", -}; - -DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction); - -static int device_append(sd_device *device, char *key, const char **_major, const char **_minor, uint64_t *_seqnum, - DeviceAction *_action) { - DeviceAction action = _DEVICE_ACTION_INVALID; - uint64_t seqnum = 0; - const char *major = NULL, *minor = NULL; - char *value; - int r; - - assert(device); - assert(key); - assert(_major); - assert(_minor); - assert(_seqnum); - assert(_action); - - value = strchr(key, '='); - if (!value) { - log_debug("sd-device: not a key-value pair: '%s'", key); - return -EINVAL; - } - - *value = '\0'; - - value++; - - if (streq(key, "MAJOR")) - major = value; - else if (streq(key, "MINOR")) - minor = value; - else { - if (streq(key, "ACTION")) { - action = device_action_from_string(value); - if (action == _DEVICE_ACTION_INVALID) - return -EINVAL; - } else if (streq(key, "SEQNUM")) { - r = safe_atou64(value, &seqnum); - if (r < 0) - return r; - else if (seqnum == 0) - /* kernel only sends seqnum > 0 */ - return -EINVAL; - } - - r = device_amend(device, key, value); - if (r < 0) - return r; - } - - if (major != 0) - *_major = major; - - if (minor != 0) - *_minor = minor; - - if (action != _DEVICE_ACTION_INVALID) - *_action = action; - - if (seqnum > 0) - *_seqnum = seqnum; - - return 0; -} - -void device_seal(sd_device *device) { - assert(device); - - device->sealed = true; -} - -static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum) { - assert(device); - - if (!device->devpath || !device->subsystem || action == _DEVICE_ACTION_INVALID || seqnum == 0) { - log_debug("sd-device: device created from strv lacks devpath, subsystem, action or seqnum"); - return -EINVAL; - } - - device->sealed = true; - - return 0; -} - -int device_new_from_strv(sd_device **ret, char **strv) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - char **key; - const char *major = NULL, *minor = NULL; - DeviceAction action = _DEVICE_ACTION_INVALID; - uint64_t seqnum; - int r; - - assert(ret); - assert(strv); - - r = device_new_aux(&device); - if (r < 0) - return r; - - STRV_FOREACH(key, strv) { - r = device_append(device, *key, &major, &minor, &seqnum, &action); - if (r < 0) - return r; - } - - if (major) { - r = device_set_devnum(device, major, minor); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor); - } - - r = device_verify(device, action, seqnum); - if (r < 0) - return r; - - *ret = device; - device = NULL; - - return 0; -} - -int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - const char *major = NULL, *minor = NULL; - DeviceAction action = _DEVICE_ACTION_INVALID; - uint64_t seqnum; - unsigned i = 0; - int r; - - assert(ret); - assert(nulstr); - assert(len); - - r = device_new_aux(&device); - if (r < 0) - return r; - - while (i < len) { - char *key; - const char *end; - - key = (char*)&nulstr[i]; - end = memchr(key, '\0', len - i); - if (!end) { - log_debug("sd-device: failed to parse nulstr"); - return -EINVAL; - } - i += end - key + 1; - - r = device_append(device, key, &major, &minor, &seqnum, &action); - if (r < 0) - return r; - } - - if (major) { - r = device_set_devnum(device, major, minor); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor); - } - - r = device_verify(device, action, seqnum); - if (r < 0) - return r; - - *ret = device; - device = NULL; - - return 0; -} - -static int device_update_properties_bufs(sd_device *device) { - const char *val, *prop; - _cleanup_free_ char **buf_strv = NULL; - _cleanup_free_ uint8_t *buf_nulstr = NULL; - size_t allocated_nulstr = 0; - size_t nulstr_len = 0, num = 0, i = 0; - - assert(device); - - if (!device->properties_buf_outdated) - return 0; - - FOREACH_DEVICE_PROPERTY(device, prop, val) { - size_t len = 0; - - len = strlen(prop) + 1 + strlen(val); - - buf_nulstr = GREEDY_REALLOC0(buf_nulstr, allocated_nulstr, nulstr_len + len + 2); - if (!buf_nulstr) - return -ENOMEM; - - strscpyl((char *)buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL); - nulstr_len += len + 1; - ++num; - } - - /* build buf_strv from buf_nulstr */ - buf_strv = new0(char *, num + 1); - if (!buf_strv) - return -ENOMEM; - - NULSTR_FOREACH(val, (char*) buf_nulstr) { - buf_strv[i] = (char *) val; - assert(i < num); - i++; - } - - free(device->properties_nulstr); - device->properties_nulstr = buf_nulstr; - buf_nulstr = NULL; - device->properties_nulstr_len = nulstr_len; - free(device->properties_strv); - device->properties_strv = buf_strv; - buf_strv = NULL; - - device->properties_buf_outdated = false; - - return 0; -} - -int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len) { - int r; - - assert(device); - assert(nulstr); - assert(len); - - r = device_update_properties_bufs(device); - if (r < 0) - return r; - - *nulstr = device->properties_nulstr; - *len = device->properties_nulstr_len; - - return 0; -} - -int device_get_properties_strv(sd_device *device, char ***strv) { - int r; - - assert(device); - assert(strv); - - r = device_update_properties_bufs(device); - if (r < 0) - return r; - - *strv = device->properties_strv; - - return 0; -} - -int device_get_devlink_priority(sd_device *device, int *priority) { - int r; - - assert(device); - assert(priority); - - r = device_read_db(device); - if (r < 0) - return r; - - *priority = device->devlink_priority; - - return 0; -} - -int device_get_watch_handle(sd_device *device, int *handle) { - int r; - - assert(device); - assert(handle); - - r = device_read_db(device); - if (r < 0) - return r; - - *handle = device->watch_handle; - - return 0; -} - -void device_set_watch_handle(sd_device *device, int handle) { - assert(device); - - device->watch_handle = handle; -} - -int device_rename(sd_device *device, const char *name) { - _cleanup_free_ char *dirname = NULL; - char *new_syspath; - const char *interface; - int r; - - assert(device); - assert(name); - - dirname = dirname_malloc(device->syspath); - if (!dirname) - return -ENOMEM; - - new_syspath = strjoina(dirname, "/", name); - - /* the user must trust that the new name is correct */ - r = device_set_syspath(device, new_syspath, false); - if (r < 0) - return r; - - r = sd_device_get_property_value(device, "INTERFACE", &interface); - if (r >= 0) { - r = device_add_property_internal(device, "INTERFACE", name); - if (r < 0) - return r; - - /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */ - r = device_add_property_internal(device, "INTERFACE_OLD", interface); - if (r < 0) - return r; - } else if (r != -ENOENT) - return r; - - return 0; -} - -int device_shallow_clone(sd_device *old_device, sd_device **new_device) { - _cleanup_(sd_device_unrefp) sd_device *ret = NULL; - int r; - - assert(old_device); - assert(new_device); - - r = device_new_aux(&ret); - if (r < 0) - return r; - - r = device_set_syspath(ret, old_device->syspath, false); - if (r < 0) - return r; - - r = device_set_subsystem(ret, old_device->subsystem); - if (r < 0) - return r; - - ret->devnum = old_device->devnum; - - *new_device = ret; - ret = NULL; - - return 0; -} - -int device_clone_with_db(sd_device *old_device, sd_device **new_device) { - _cleanup_(sd_device_unrefp) sd_device *ret = NULL; - int r; - - assert(old_device); - assert(new_device); - - r = device_shallow_clone(old_device, &ret); - if (r < 0) - return r; - - r = device_read_db(ret); - if (r < 0) - return r; - - ret->sealed = true; - - *new_device = ret; - ret = NULL; - - return 0; -} - -int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) { - _cleanup_(sd_device_unrefp) sd_device *ret = NULL; - int r; - - assert(new_device); - assert(syspath); - assert(action); - - r = sd_device_new_from_syspath(&ret, syspath); - if (r < 0) - return r; - - r = device_read_uevent_file(ret); - if (r < 0) - return r; - - r = device_add_property_internal(ret, "ACTION", action); - if (r < 0) - return r; - - *new_device = ret; - ret = NULL; - - return 0; -} - -int device_copy_properties(sd_device *device_dst, sd_device *device_src) { - const char *property, *value; - int r; - - assert(device_dst); - assert(device_src); - - FOREACH_DEVICE_PROPERTY(device_src, property, value) { - r = device_add_property(device_dst, property, value); - if (r < 0) - return r; - } - - return 0; -} - -void device_cleanup_tags(sd_device *device) { - assert(device); - - set_free_free(device->tags); - device->tags = NULL; - device->property_tags_outdated = true; - device->tags_generation++; -} - -void device_cleanup_devlinks(sd_device *device) { - assert(device); - - set_free_free(device->devlinks); - device->devlinks = NULL; - device->property_devlinks_outdated = true; - device->devlinks_generation++; -} - -void device_remove_tag(sd_device *device, const char *tag) { - assert(device); - assert(tag); - - free(set_remove(device->tags, tag)); - device->property_tags_outdated = true; - device->tags_generation++; -} - -static int device_tag(sd_device *device, const char *tag, bool add) { - const char *id; - char *path; - int r; - - assert(device); - assert(tag); - - r = device_get_id_filename(device, &id); - if (r < 0) - return r; - - path = strjoina("/run/udev/tags/", tag, "/", id); - - if (add) { - r = touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444); - if (r < 0) - return r; - } else { - r = unlink(path); - if (r < 0 && errno != ENOENT) - return -errno; - } - - return 0; -} - -int device_tag_index(sd_device *device, sd_device *device_old, bool add) { - const char *tag; - int r = 0, k; - - if (add && device_old) { - /* delete possible left-over tags */ - FOREACH_DEVICE_TAG(device_old, tag) { - if (!sd_device_has_tag(device, tag)) { - k = device_tag(device_old, tag, false); - if (r >= 0 && k < 0) - r = k; - } - } - } - - FOREACH_DEVICE_TAG(device, tag) { - k = device_tag(device, tag, add); - if (r >= 0 && k < 0) - r = k; - } - - return r; -} - -static bool device_has_info(sd_device *device) { - assert(device); - - if (!set_isempty(device->devlinks)) - return true; - - if (device->devlink_priority != 0) - return true; - - if (!ordered_hashmap_isempty(device->properties_db)) - return true; - - if (!set_isempty(device->tags)) - return true; - - if (device->watch_handle >= 0) - return true; - - return false; -} - -void device_set_db_persist(sd_device *device) { - assert(device); - - device->db_persist = true; -} - -int device_update_db(sd_device *device) { - const char *id; - char *path; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *path_tmp = NULL; - bool has_info; - int r; - - assert(device); - - has_info = device_has_info(device); - - r = device_get_id_filename(device, &id); - if (r < 0) - return r; - - path = strjoina("/run/udev/data/", id); - - /* do not store anything for otherwise empty devices */ - if (!has_info && major(device->devnum) == 0 && device->ifindex == 0) { - r = unlink(path); - if (r < 0 && errno != ENOENT) - return -errno; - - return 0; - } - - /* write a database file */ - r = mkdir_parents(path, 0755); - if (r < 0) - return r; - - r = fopen_temporary(path, &f, &path_tmp); - if (r < 0) - return r; - - /* - * set 'sticky' bit to indicate that we should not clean the - * database when we transition from initramfs to the real root - */ - if (device->db_persist) { - r = fchmod(fileno(f), 01644); - if (r < 0) { - r = -errno; - goto fail; - } - } else { - r = fchmod(fileno(f), 0644); - if (r < 0) { - r = -errno; - goto fail; - } - } - - if (has_info) { - const char *property, *value, *tag; - Iterator i; - - if (major(device->devnum) > 0) { - const char *devlink; - - FOREACH_DEVICE_DEVLINK(device, devlink) - fprintf(f, "S:%s\n", devlink + strlen("/dev/")); - - if (device->devlink_priority != 0) - fprintf(f, "L:%i\n", device->devlink_priority); - - if (device->watch_handle >= 0) - fprintf(f, "W:%i\n", device->watch_handle); - } - - if (device->usec_initialized > 0) - fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized); - - ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db, i) - fprintf(f, "E:%s=%s\n", property, value); - - FOREACH_DEVICE_TAG(device, tag) - fprintf(f, "G:%s\n", tag); - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - r = rename(path_tmp, path); - if (r < 0) { - r = -errno; - goto fail; - } - - log_debug("created %s file '%s' for '%s'", has_info ? "db" : "empty", - path, device->devpath); - - return 0; - -fail: - (void) unlink(path); - (void) unlink(path_tmp); - - return log_error_errno(r, "failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", path, device->devpath); -} - -int device_delete_db(sd_device *device) { - const char *id; - char *path; - int r; - - assert(device); - - r = device_get_id_filename(device, &id); - if (r < 0) - return r; - - path = strjoina("/run/udev/data/", id); - - r = unlink(path); - if (r < 0 && errno != ENOENT) - return -errno; - - return 0; -} - -int device_read_db_force(sd_device *device) { - assert(device); - - return device_read_db_aux(device, true); -} diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h deleted file mode 100644 index 29b3e155fb..0000000000 --- a/src/libsystemd/sd-device/device-private.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen - - 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 . -***/ - -#include -#include -#include - -#include "sd-device.h" - -int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len); -int device_new_from_strv(sd_device **ret, char **strv); - -int device_get_id_filename(sd_device *device, const char **ret); - -int device_get_devlink_priority(sd_device *device, int *priority); -int device_get_watch_handle(sd_device *device, int *handle); -int device_get_devnode_mode(sd_device *device, mode_t *mode); -int device_get_devnode_uid(sd_device *device, uid_t *uid); -int device_get_devnode_gid(sd_device *device, gid_t *gid); - -void device_seal(sd_device *device); -void device_set_is_initialized(sd_device *device); -void device_set_watch_handle(sd_device *device, int fd); -void device_set_db_persist(sd_device *device); -void device_set_devlink_priority(sd_device *device, int priority); -int device_ensure_usec_initialized(sd_device *device, sd_device *device_old); -int device_add_devlink(sd_device *device, const char *devlink); -int device_add_property(sd_device *device, const char *property, const char *value); -int device_add_tag(sd_device *device, const char *tag); -void device_remove_tag(sd_device *device, const char *tag); -void device_cleanup_tags(sd_device *device); -void device_cleanup_devlinks(sd_device *device); - -uint64_t device_get_properties_generation(sd_device *device); -uint64_t device_get_tags_generation(sd_device *device); -uint64_t device_get_devlinks_generation(sd_device *device); - -int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len); -int device_get_properties_strv(sd_device *device, char ***strv); - -int device_rename(sd_device *device, const char *name); -int device_shallow_clone(sd_device *old_device, sd_device **new_device); -int device_clone_with_db(sd_device *old_device, sd_device **new_device); -int device_copy_properties(sd_device *device_dst, sd_device *device_src); -int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action); - -int device_tag_index(sd_device *dev, sd_device *dev_old, bool add); -int device_update_db(sd_device *device); -int device_delete_db(sd_device *device); -int device_read_db_force(sd_device *device); diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h deleted file mode 100644 index 5b42e11de6..0000000000 --- a/src/libsystemd/sd-device/device-util.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014-2015 Tom Gundersen - - 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 . -***/ - -#include "util.h" - -#define FOREACH_DEVICE_PROPERTY(device, key, value) \ - for (key = sd_device_get_property_first(device, &(value)); \ - key; \ - key = sd_device_get_property_next(device, &(value))) - -#define FOREACH_DEVICE_TAG(device, tag) \ - for (tag = sd_device_get_tag_first(device); \ - tag; \ - tag = sd_device_get_tag_next(device)) - -#define FOREACH_DEVICE_SYSATTR(device, attr) \ - for (attr = sd_device_get_sysattr_first(device); \ - attr; \ - attr = sd_device_get_sysattr_next(device)) - -#define FOREACH_DEVICE_DEVLINK(device, devlink) \ - for (devlink = sd_device_get_devlink_first(device); \ - devlink; \ - devlink = sd_device_get_devlink_next(device)) - -#define FOREACH_DEVICE(enumerator, device) \ - for (device = sd_device_enumerator_get_device_first(enumerator); \ - device; \ - device = sd_device_enumerator_get_device_next(enumerator)) - -#define FOREACH_SUBSYSTEM(enumerator, device) \ - for (device = sd_device_enumerator_get_subsystem_first(enumerator); \ - device; \ - device = sd_device_enumerator_get_subsystem_next(enumerator)) diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c deleted file mode 100644 index b1c3d5f228..0000000000 --- a/src/libsystemd/sd-device/sd-device.c +++ /dev/null @@ -1,1884 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - Copyright 2014 Tom Gundersen - - 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 . -***/ - -#include -#include -#include - -#include "sd-device.h" - -#include "alloc-util.h" -#include "device-internal.h" -#include "device-private.h" -#include "device-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hashmap.h" -#include "macro.h" -#include "parse-util.h" -#include "path-util.h" -#include "set.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "strxcpyx.h" -#include "util.h" - -int device_new_aux(sd_device **ret) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - - assert(ret); - - device = new0(sd_device, 1); - if (!device) - return -ENOMEM; - - device->n_ref = 1; - device->watch_handle = -1; - - *ret = device; - device = NULL; - - return 0; -} - -_public_ sd_device *sd_device_ref(sd_device *device) { - if (device) - assert_se(++ device->n_ref >= 2); - - return device; -} - -_public_ sd_device *sd_device_unref(sd_device *device) { - if (device && -- device->n_ref == 0) { - sd_device_unref(device->parent); - free(device->syspath); - free(device->sysname); - free(device->devtype); - free(device->devname); - free(device->subsystem); - free(device->driver); - free(device->id_filename); - free(device->properties_strv); - free(device->properties_nulstr); - - ordered_hashmap_free_free_free(device->properties); - ordered_hashmap_free_free_free(device->properties_db); - hashmap_free_free_free(device->sysattr_values); - set_free_free(device->sysattrs); - set_free_free(device->tags); - set_free_free(device->devlinks); - - free(device); - } - - return NULL; -} - -int device_add_property_aux(sd_device *device, const char *_key, const char *_value, bool db) { - OrderedHashmap **properties; - - assert(device); - assert(_key); - - if (db) - properties = &device->properties_db; - else - properties = &device->properties; - - if (_value) { - _cleanup_free_ char *key = NULL, *value = NULL, *old_key = NULL, *old_value = NULL; - int r; - - r = ordered_hashmap_ensure_allocated(properties, &string_hash_ops); - if (r < 0) - return r; - - key = strdup(_key); - if (!key) - return -ENOMEM; - - value = strdup(_value); - if (!value) - return -ENOMEM; - - old_value = ordered_hashmap_get2(*properties, key, (void**) &old_key); - - r = ordered_hashmap_replace(*properties, key, value); - if (r < 0) - return r; - - key = NULL; - value = NULL; - } else { - _cleanup_free_ char *key = NULL; - _cleanup_free_ char *value = NULL; - - value = ordered_hashmap_remove2(*properties, _key, (void**) &key); - } - - if (!db) { - device->properties_generation++; - device->properties_buf_outdated = true; - } - - return 0; -} - -int device_add_property_internal(sd_device *device, const char *key, const char *value) { - return device_add_property_aux(device, key, value, false); -} - -int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { - _cleanup_free_ char *syspath = NULL; - const char *devpath; - int r; - - assert(device); - assert(_syspath); - - /* must be a subdirectory of /sys */ - if (!path_startswith(_syspath, "/sys/")) { - log_debug("sd-device: syspath '%s' is not a subdirectory of /sys", _syspath); - return -EINVAL; - } - - if (verify) { - r = readlink_and_canonicalize(_syspath, &syspath); - if (r == -ENOENT) - /* the device does not exist (any more?) */ - return -ENODEV; - else if (r == -EINVAL) { - /* not a symlink */ - syspath = canonicalize_file_name(_syspath); - if (!syspath) { - if (errno == ENOENT) - /* the device does not exist (any more?) */ - return -ENODEV; - - return log_debug_errno(errno, "sd-device: could not canonicalize '%s': %m", _syspath); - } - } else if (r < 0) { - log_debug_errno(r, "sd-device: could not get target of '%s': %m", _syspath); - return r; - } - - if (path_startswith(syspath, "/sys/devices/")) { - char *path; - - /* all 'devices' require an 'uevent' file */ - path = strjoina(syspath, "/uevent"); - r = access(path, F_OK); - if (r < 0) { - if (errno == ENOENT) - /* this is not a valid device */ - return -ENODEV; - - log_debug("sd-device: %s does not have an uevent file: %m", syspath); - return -errno; - } - } else { - /* everything else just just needs to be a directory */ - if (!is_dir(syspath, false)) - return -ENODEV; - } - } else { - syspath = strdup(_syspath); - if (!syspath) - return -ENOMEM; - } - - devpath = syspath + strlen("/sys"); - - r = device_add_property_internal(device, "DEVPATH", devpath); - if (r < 0) - return r; - - free(device->syspath); - device->syspath = syspath; - syspath = NULL; - - device->devpath = devpath; - - return 0; -} - -_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - int r; - - assert_return(ret, -EINVAL); - assert_return(syspath, -EINVAL); - - r = device_new_aux(&device); - if (r < 0) - return r; - - r = device_set_syspath(device, syspath, true); - if (r < 0) - return r; - - *ret = device; - device = NULL; - - return 0; -} - -_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) { - char *syspath; - char id[DECIMAL_STR_MAX(unsigned) * 2 + 1]; - - assert_return(ret, -EINVAL); - assert_return(type == 'b' || type == 'c', -EINVAL); - - /* use /sys/dev/{block,char}/: link */ - snprintf(id, sizeof(id), "%u:%u", major(devnum), minor(devnum)); - - syspath = strjoina("/sys/dev/", (type == 'b' ? "block" : "char"), "/", id); - - return sd_device_new_from_syspath(ret, syspath); -} - -_public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname) { - char *syspath; - - assert_return(ret, -EINVAL); - assert_return(subsystem, -EINVAL); - assert_return(sysname, -EINVAL); - - if (streq(subsystem, "subsystem")) { - syspath = strjoina("/sys/subsystem/", sysname); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - - syspath = strjoina("/sys/bus/", sysname); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - - syspath = strjoina("/sys/class/", sysname); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - } else if (streq(subsystem, "module")) { - syspath = strjoina("/sys/module/", sysname); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - } else if (streq(subsystem, "drivers")) { - char subsys[PATH_MAX]; - char *driver; - - strscpy(subsys, sizeof(subsys), sysname); - driver = strchr(subsys, ':'); - if (driver) { - driver[0] = '\0'; - driver++; - - syspath = strjoina("/sys/subsystem/", subsys, "/drivers/", driver); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - - syspath = strjoina("/sys/bus/", subsys, "/drivers/", driver); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - } else - return -EINVAL; - } else { - char *name; - size_t len = 0; - - /* translate sysname back to sysfs filename */ - name = strdupa(sysname); - while (name[len] != '\0') { - if (name[len] == '/') - name[len] = '!'; - - len++; - } - - syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", name); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - - syspath = strjoina("/sys/bus/", subsystem, "/devices/", name); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - - syspath = strjoina("/sys/class/", subsystem, "/", name); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - } - - return -ENODEV; -} - -int device_set_devtype(sd_device *device, const char *_devtype) { - _cleanup_free_ char *devtype = NULL; - int r; - - assert(device); - assert(_devtype); - - devtype = strdup(_devtype); - if (!devtype) - return -ENOMEM; - - r = device_add_property_internal(device, "DEVTYPE", devtype); - if (r < 0) - return r; - - free(device->devtype); - device->devtype = devtype; - devtype = NULL; - - return 0; -} - -int device_set_ifindex(sd_device *device, const char *_ifindex) { - int ifindex, r; - - assert(device); - assert(_ifindex); - - r = parse_ifindex(_ifindex, &ifindex); - if (r < 0) - return r; - - r = device_add_property_internal(device, "IFINDEX", _ifindex); - if (r < 0) - return r; - - device->ifindex = ifindex; - - return 0; -} - -int device_set_devname(sd_device *device, const char *_devname) { - _cleanup_free_ char *devname = NULL; - int r; - - assert(device); - assert(_devname); - - if (_devname[0] != '/') { - r = asprintf(&devname, "/dev/%s", _devname); - if (r < 0) - return -ENOMEM; - } else { - devname = strdup(_devname); - if (!devname) - return -ENOMEM; - } - - r = device_add_property_internal(device, "DEVNAME", devname); - if (r < 0) - return r; - - free(device->devname); - device->devname = devname; - devname = NULL; - - return 0; -} - -int device_set_devmode(sd_device *device, const char *_devmode) { - unsigned devmode; - int r; - - assert(device); - assert(_devmode); - - r = safe_atou(_devmode, &devmode); - if (r < 0) - return r; - - if (devmode > 07777) - return -EINVAL; - - r = device_add_property_internal(device, "DEVMODE", _devmode); - if (r < 0) - return r; - - device->devmode = devmode; - - return 0; -} - -int device_set_devnum(sd_device *device, const char *major, const char *minor) { - unsigned maj = 0, min = 0; - int r; - - assert(device); - assert(major); - - r = safe_atou(major, &maj); - if (r < 0) - return r; - if (!maj) - return 0; - - if (minor) { - r = safe_atou(minor, &min); - if (r < 0) - return r; - } - - r = device_add_property_internal(device, "MAJOR", major); - if (r < 0) - return r; - - if (minor) { - r = device_add_property_internal(device, "MINOR", minor); - if (r < 0) - return r; - } - - device->devnum = makedev(maj, min); - - return 0; -} - -static int handle_uevent_line(sd_device *device, const char *key, const char *value, const char **major, const char **minor) { - int r; - - assert(device); - assert(key); - assert(value); - assert(major); - assert(minor); - - if (streq(key, "DEVTYPE")) { - r = device_set_devtype(device, value); - if (r < 0) - return r; - } else if (streq(key, "IFINDEX")) { - r = device_set_ifindex(device, value); - if (r < 0) - return r; - } else if (streq(key, "DEVNAME")) { - r = device_set_devname(device, value); - if (r < 0) - return r; - } else if (streq(key, "DEVMODE")) { - r = device_set_devmode(device, value); - if (r < 0) - return r; - } else if (streq(key, "MAJOR")) - *major = value; - else if (streq(key, "MINOR")) - *minor = value; - else { - r = device_add_property_internal(device, key, value); - if (r < 0) - return r; - } - - return 0; -} - -int device_read_uevent_file(sd_device *device) { - _cleanup_free_ char *uevent = NULL; - const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL; - char *path; - size_t uevent_len; - unsigned i; - int r; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - INVALID_LINE, - } state = PRE_KEY; - - assert(device); - - if (device->uevent_loaded || device->sealed) - return 0; - - device->uevent_loaded = true; - - r = sd_device_get_syspath(device, &syspath); - if (r < 0) - return r; - - path = strjoina(syspath, "/uevent"); - - r = read_full_file(path, &uevent, &uevent_len); - if (r == -EACCES) - /* empty uevent files may be write-only */ - return 0; - else if (r == -ENOENT) - /* some devices may not have uevent files, see set_syspath() */ - return 0; - else if (r < 0) { - log_debug_errno(r, "sd-device: failed to read uevent file '%s': %m", path); - return r; - } - - for (i = 0; i < uevent_len; i++) { - switch (state) { - case PRE_KEY: - if (!strchr(NEWLINE, uevent[i])) { - key = &uevent[i]; - - state = KEY; - } - - break; - case KEY: - if (uevent[i] == '=') { - uevent[i] = '\0'; - - state = PRE_VALUE; - } else if (strchr(NEWLINE, uevent[i])) { - uevent[i] = '\0'; - log_debug("sd-device: ignoring invalid uevent line '%s'", key); - - state = PRE_KEY; - } - - break; - case PRE_VALUE: - value = &uevent[i]; - - state = VALUE; - - break; - case VALUE: - if (strchr(NEWLINE, uevent[i])) { - uevent[i] = '\0'; - - r = handle_uevent_line(device, key, value, &major, &minor); - if (r < 0) - log_debug_errno(r, "sd-device: failed to handle uevent entry '%s=%s': %m", key, value); - - state = PRE_KEY; - } - - break; - default: - assert_not_reached("invalid state when parsing uevent file"); - } - } - - if (major) { - r = device_set_devnum(device, major, minor); - if (r < 0) - log_debug_errno(r, "sd-device: could not set 'MAJOR=%s' or 'MINOR=%s' from '%s': %m", major, minor, path); - } - - return 0; -} - -_public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) { - int r; - - assert_return(device, -EINVAL); - assert_return(ifindex, -EINVAL); - - r = device_read_uevent_file(device); - if (r < 0) - return r; - - *ifindex = device->ifindex; - - return 0; -} - -_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) { - int r; - - assert_return(ret, -EINVAL); - assert_return(id, -EINVAL); - - switch (id[0]) { - case 'b': - case 'c': - { - char type; - int maj, min; - - r = sscanf(id, "%c%i:%i", &type, &maj, &min); - if (r != 3) - return -EINVAL; - - return sd_device_new_from_devnum(ret, type, makedev(maj, min)); - } - case 'n': - { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - _cleanup_close_ int sk = -1; - struct ifreq ifr = {}; - int ifindex; - - r = parse_ifindex(&id[1], &ifr.ifr_ifindex); - if (r < 0) - return r; - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -errno; - - r = ioctl(sk, SIOCGIFNAME, &ifr); - if (r < 0) - return -errno; - - r = sd_device_new_from_subsystem_sysname(&device, "net", ifr.ifr_name); - if (r < 0) - return r; - - r = sd_device_get_ifindex(device, &ifindex); - if (r < 0) - return r; - - /* this is racey, so we might end up with the wrong device */ - if (ifr.ifr_ifindex != ifindex) - return -ENODEV; - - *ret = device; - device = NULL; - - return 0; - } - case '+': - { - char subsys[PATH_MAX]; - char *sysname; - - (void)strscpy(subsys, sizeof(subsys), id + 1); - sysname = strchr(subsys, ':'); - if (!sysname) - return -EINVAL; - - sysname[0] = '\0'; - sysname++; - - return sd_device_new_from_subsystem_sysname(ret, subsys, sysname); - } - default: - return -EINVAL; - } -} - -_public_ int sd_device_get_syspath(sd_device *device, const char **ret) { - assert_return(device, -EINVAL); - assert_return(ret, -EINVAL); - - assert(path_startswith(device->syspath, "/sys/")); - - *ret = device->syspath; - - return 0; -} - -static int device_new_from_child(sd_device **ret, sd_device *child) { - _cleanup_free_ char *path = NULL; - const char *subdir, *syspath; - int r; - - assert(ret); - assert(child); - - r = sd_device_get_syspath(child, &syspath); - if (r < 0) - return r; - - path = strdup(syspath); - if (!path) - return -ENOMEM; - subdir = path + strlen("/sys"); - - for (;;) { - char *pos; - - pos = strrchr(subdir, '/'); - if (!pos || pos < subdir + 2) - break; - - *pos = '\0'; - - r = sd_device_new_from_syspath(ret, path); - if (r < 0) - continue; - - return 0; - } - - return -ENODEV; -} - -_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) { - - assert_return(ret, -EINVAL); - assert_return(child, -EINVAL); - - if (!child->parent_set) { - child->parent_set = true; - - (void)device_new_from_child(&child->parent, child); - } - - if (!child->parent) - return -ENOENT; - - *ret = child->parent; - - return 0; -} - -int device_set_subsystem(sd_device *device, const char *_subsystem) { - _cleanup_free_ char *subsystem = NULL; - int r; - - assert(device); - assert(_subsystem); - - subsystem = strdup(_subsystem); - if (!subsystem) - return -ENOMEM; - - r = device_add_property_internal(device, "SUBSYSTEM", subsystem); - if (r < 0) - return r; - - free(device->subsystem); - device->subsystem = subsystem; - subsystem = NULL; - - device->subsystem_set = true; - - return 0; -} - -_public_ int sd_device_get_subsystem(sd_device *device, const char **ret) { - assert_return(ret, -EINVAL); - assert_return(device, -EINVAL); - - if (!device->subsystem_set) { - _cleanup_free_ char *subsystem = NULL; - const char *syspath; - char *path; - int r; - - /* read 'subsystem' link */ - r = sd_device_get_syspath(device, &syspath); - if (r < 0) - return r; - - path = strjoina(syspath, "/subsystem"); - r = readlink_value(path, &subsystem); - if (r >= 0) - r = device_set_subsystem(device, subsystem); - /* use implicit names */ - else if (path_startswith(device->devpath, "/module/")) - r = device_set_subsystem(device, "module"); - else if (strstr(device->devpath, "/drivers/")) - r = device_set_subsystem(device, "drivers"); - else if (path_startswith(device->devpath, "/subsystem/") || - path_startswith(device->devpath, "/class/") || - path_startswith(device->devpath, "/bus/")) - r = device_set_subsystem(device, "subsystem"); - if (r < 0 && r != -ENOENT) - return log_debug_errno(r, "sd-device: could not set subsystem for %s: %m", device->devpath); - - device->subsystem_set = true; - } - - if (!device->subsystem) - return -ENOENT; - - *ret = device->subsystem; - - return 0; -} - -_public_ int sd_device_get_devtype(sd_device *device, const char **devtype) { - int r; - - assert(devtype); - assert(device); - - r = device_read_uevent_file(device); - if (r < 0) - return r; - - *devtype = device->devtype; - - return 0; -} - -_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) { - sd_device *parent = NULL; - int r; - - assert_return(child, -EINVAL); - assert_return(subsystem, -EINVAL); - - r = sd_device_get_parent(child, &parent); - while (r >= 0) { - const char *parent_subsystem = NULL; - const char *parent_devtype = NULL; - - (void)sd_device_get_subsystem(parent, &parent_subsystem); - if (streq_ptr(parent_subsystem, subsystem)) { - if (!devtype) - break; - - (void)sd_device_get_devtype(parent, &parent_devtype); - if (streq_ptr(parent_devtype, devtype)) - break; - } - r = sd_device_get_parent(parent, &parent); - } - - if (r < 0) - return r; - - *ret = parent; - - return 0; -} - -_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) { - int r; - - assert_return(device, -EINVAL); - assert_return(devnum, -EINVAL); - - r = device_read_uevent_file(device); - if (r < 0) - return r; - - *devnum = device->devnum; - - return 0; -} - -int device_set_driver(sd_device *device, const char *_driver) { - _cleanup_free_ char *driver = NULL; - int r; - - assert(device); - assert(_driver); - - driver = strdup(_driver); - if (!driver) - return -ENOMEM; - - r = device_add_property_internal(device, "DRIVER", driver); - if (r < 0) - return r; - - free(device->driver); - device->driver = driver; - driver = NULL; - - device->driver_set = true; - - return 0; -} - -_public_ int sd_device_get_driver(sd_device *device, const char **ret) { - assert_return(device, -EINVAL); - assert_return(ret, -EINVAL); - - if (!device->driver_set) { - _cleanup_free_ char *driver = NULL; - const char *syspath; - char *path; - int r; - - r = sd_device_get_syspath(device, &syspath); - if (r < 0) - return r; - - path = strjoina(syspath, "/driver"); - r = readlink_value(path, &driver); - if (r >= 0) { - r = device_set_driver(device, driver); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath); - } else if (r == -ENOENT) - device->driver_set = true; - else - return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath); - } - - if (!device->driver) - return -ENOENT; - - *ret = device->driver; - - return 0; -} - -_public_ int sd_device_get_devpath(sd_device *device, const char **devpath) { - assert_return(device, -EINVAL); - assert_return(devpath, -EINVAL); - - assert(device->devpath); - assert(device->devpath[0] == '/'); - - *devpath = device->devpath; - - return 0; -} - -_public_ int sd_device_get_devname(sd_device *device, const char **devname) { - int r; - - assert_return(device, -EINVAL); - assert_return(devname, -EINVAL); - - r = device_read_uevent_file(device); - if (r < 0) - return r; - - if (!device->devname) - return -ENOENT; - - assert(path_startswith(device->devname, "/dev/")); - - *devname = device->devname; - - return 0; -} - -static int device_set_sysname(sd_device *device) { - _cleanup_free_ char *sysname = NULL; - const char *sysnum = NULL; - const char *pos; - size_t len = 0; - - pos = strrchr(device->devpath, '/'); - if (!pos) - return -EINVAL; - pos++; - - /* devpath is not a root directory */ - if (*pos == '\0' || pos <= device->devpath) - return -EINVAL; - - sysname = strdup(pos); - if (!sysname) - return -ENOMEM; - - /* some devices have '!' in their name, change that to '/' */ - while (sysname[len] != '\0') { - if (sysname[len] == '!') - sysname[len] = '/'; - - len++; - } - - /* trailing number */ - while (len > 0 && isdigit(sysname[--len])) - sysnum = &sysname[len]; - - if (len == 0) - sysnum = NULL; - - free(device->sysname); - device->sysname = sysname; - sysname = NULL; - - device->sysnum = sysnum; - - device->sysname_set = true; - - return 0; -} - -_public_ int sd_device_get_sysname(sd_device *device, const char **ret) { - int r; - - assert_return(device, -EINVAL); - assert_return(ret, -EINVAL); - - if (!device->sysname_set) { - r = device_set_sysname(device); - if (r < 0) - return r; - } - - assert_return(device->sysname, -ENOENT); - - *ret = device->sysname; - - return 0; -} - -_public_ int sd_device_get_sysnum(sd_device *device, const char **ret) { - int r; - - assert_return(device, -EINVAL); - assert_return(ret, -EINVAL); - - if (!device->sysname_set) { - r = device_set_sysname(device); - if (r < 0) - return r; - } - - *ret = device->sysnum; - - return 0; -} - -static bool is_valid_tag(const char *tag) { - assert(tag); - - return !strchr(tag, ':') && !strchr(tag, ' '); -} - -int device_add_tag(sd_device *device, const char *tag) { - int r; - - assert(device); - assert(tag); - - if (!is_valid_tag(tag)) - return -EINVAL; - - r = set_ensure_allocated(&device->tags, &string_hash_ops); - if (r < 0) - return r; - - r = set_put_strdup(device->tags, tag); - if (r < 0) - return r; - - device->tags_generation++; - device->property_tags_outdated = true; - - return 0; -} - -int device_add_devlink(sd_device *device, const char *devlink) { - int r; - - assert(device); - assert(devlink); - - r = set_ensure_allocated(&device->devlinks, &string_hash_ops); - if (r < 0) - return r; - - r = set_put_strdup(device->devlinks, devlink); - if (r < 0) - return r; - - device->devlinks_generation++; - device->property_devlinks_outdated = true; - - return 0; -} - -static int device_add_property_internal_from_string(sd_device *device, const char *str) { - _cleanup_free_ char *key = NULL; - char *value; - - assert(device); - assert(str); - - key = strdup(str); - if (!key) - return -ENOMEM; - - value = strchr(key, '='); - if (!value) - return -EINVAL; - - *value = '\0'; - - if (isempty(++value)) - value = NULL; - - return device_add_property_internal(device, key, value); -} - -int device_set_usec_initialized(sd_device *device, const char *initialized) { - uint64_t usec_initialized; - int r; - - assert(device); - assert(initialized); - - r = safe_atou64(initialized, &usec_initialized); - if (r < 0) - return r; - - r = device_add_property_internal(device, "USEC_INITIALIZED", initialized); - if (r < 0) - return r; - - device->usec_initialized = usec_initialized; - - return 0; -} - -static int handle_db_line(sd_device *device, char key, const char *value) { - char *path; - int r; - - assert(device); - assert(value); - - switch (key) { - case 'G': - r = device_add_tag(device, value); - if (r < 0) - return r; - - break; - case 'S': - path = strjoina("/dev/", value); - r = device_add_devlink(device, path); - if (r < 0) - return r; - - break; - case 'E': - r = device_add_property_internal_from_string(device, value); - if (r < 0) - return r; - - break; - case 'I': - r = device_set_usec_initialized(device, value); - if (r < 0) - return r; - - break; - case 'L': - r = safe_atoi(value, &device->devlink_priority); - if (r < 0) - return r; - - break; - case 'W': - r = safe_atoi(value, &device->watch_handle); - if (r < 0) - return r; - - break; - default: - log_debug("device db: unknown key '%c'", key); - } - - return 0; -} - -int device_get_id_filename(sd_device *device, const char **ret) { - assert(device); - assert(ret); - - if (!device->id_filename) { - _cleanup_free_ char *id = NULL; - const char *subsystem; - dev_t devnum; - int ifindex, r; - - r = sd_device_get_subsystem(device, &subsystem); - if (r < 0) - return r; - - r = sd_device_get_devnum(device, &devnum); - if (r < 0) - return r; - - r = sd_device_get_ifindex(device, &ifindex); - if (r < 0) - return r; - - if (major(devnum) > 0) { - assert(subsystem); - - /* use dev_t — b259:131072, c254:0 */ - r = asprintf(&id, "%c%u:%u", - streq(subsystem, "block") ? 'b' : 'c', - major(devnum), minor(devnum)); - if (r < 0) - return -ENOMEM; - } else if (ifindex > 0) { - /* use netdev ifindex — n3 */ - r = asprintf(&id, "n%u", ifindex); - if (r < 0) - return -ENOMEM; - } else { - /* use $subsys:$sysname — pci:0000:00:1f.2 - * sysname() has '!' translated, get it from devpath - */ - const char *sysname; - - sysname = basename(device->devpath); - if (!sysname) - return -EINVAL; - - if (!subsystem) - return -EINVAL; - - r = asprintf(&id, "+%s:%s", subsystem, sysname); - if (r < 0) - return -ENOMEM; - } - - device->id_filename = id; - id = NULL; - } - - *ret = device->id_filename; - - return 0; -} - -int device_read_db_aux(sd_device *device, bool force) { - _cleanup_free_ char *db = NULL; - char *path; - const char *id, *value; - char key; - size_t db_len; - unsigned i; - int r; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - INVALID_LINE, - } state = PRE_KEY; - - if (device->db_loaded || (!force && device->sealed)) - return 0; - - device->db_loaded = true; - - r = device_get_id_filename(device, &id); - if (r < 0) - return r; - - path = strjoina("/run/udev/data/", id); - - r = read_full_file(path, &db, &db_len); - if (r < 0) { - if (r == -ENOENT) - return 0; - else - return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path); - } - - /* devices with a database entry are initialized */ - device->is_initialized = true; - - for (i = 0; i < db_len; i++) { - switch (state) { - case PRE_KEY: - if (!strchr(NEWLINE, db[i])) { - key = db[i]; - - state = KEY; - } - - break; - case KEY: - if (db[i] != ':') { - log_debug("sd-device: ignoring invalid db entry with key '%c'", key); - - state = INVALID_LINE; - } else { - db[i] = '\0'; - - state = PRE_VALUE; - } - - break; - case PRE_VALUE: - value = &db[i]; - - state = VALUE; - - break; - case INVALID_LINE: - if (strchr(NEWLINE, db[i])) - state = PRE_KEY; - - break; - case VALUE: - if (strchr(NEWLINE, db[i])) { - db[i] = '\0'; - r = handle_db_line(device, key, value); - if (r < 0) - log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value); - - state = PRE_KEY; - } - - break; - default: - assert_not_reached("invalid state when parsing db"); - } - } - - return 0; -} - -static int device_read_db(sd_device *device) { - return device_read_db_aux(device, false); -} - -_public_ int sd_device_get_is_initialized(sd_device *device, int *initialized) { - int r; - - assert_return(device, -EINVAL); - assert_return(initialized, -EINVAL); - - r = device_read_db(device); - if (r < 0) - return r; - - *initialized = device->is_initialized; - - return 0; -} - -_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec) { - usec_t now_ts; - int r; - - assert_return(device, -EINVAL); - assert_return(usec, -EINVAL); - - r = device_read_db(device); - if (r < 0) - return r; - - if (!device->is_initialized) - return -EBUSY; - - if (!device->usec_initialized) - return -ENODATA; - - now_ts = now(clock_boottime_or_monotonic()); - - if (now_ts < device->usec_initialized) - return -EIO; - - *usec = now_ts - device->usec_initialized; - - return 0; -} - -_public_ const char *sd_device_get_tag_first(sd_device *device) { - void *v; - - assert_return(device, NULL); - - (void) device_read_db(device); - - device->tags_iterator_generation = device->tags_generation; - device->tags_iterator = ITERATOR_FIRST; - - (void) set_iterate(device->tags, &device->tags_iterator, &v); - return v; -} - -_public_ const char *sd_device_get_tag_next(sd_device *device) { - void *v; - - assert_return(device, NULL); - - (void) device_read_db(device); - - if (device->tags_iterator_generation != device->tags_generation) - return NULL; - - (void) set_iterate(device->tags, &device->tags_iterator, &v); - return v; -} - -_public_ const char *sd_device_get_devlink_first(sd_device *device) { - void *v; - - assert_return(device, NULL); - - (void) device_read_db(device); - - device->devlinks_iterator_generation = device->devlinks_generation; - device->devlinks_iterator = ITERATOR_FIRST; - - (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v); - return v; -} - -_public_ const char *sd_device_get_devlink_next(sd_device *device) { - void *v; - - assert_return(device, NULL); - - (void) device_read_db(device); - - if (device->devlinks_iterator_generation != device->devlinks_generation) - return NULL; - - (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v); - return v; -} - -static int device_properties_prepare(sd_device *device) { - int r; - - assert(device); - - r = device_read_uevent_file(device); - if (r < 0) - return r; - - r = device_read_db(device); - if (r < 0) - return r; - - if (device->property_devlinks_outdated) { - _cleanup_free_ char *devlinks = NULL; - size_t devlinks_allocated = 0, devlinks_len = 0; - const char *devlink; - - for (devlink = sd_device_get_devlink_first(device); devlink; devlink = sd_device_get_devlink_next(device)) { - char *e; - - if (!GREEDY_REALLOC(devlinks, devlinks_allocated, devlinks_len + strlen(devlink) + 2)) - return -ENOMEM; - if (devlinks_len > 0) - stpcpy(devlinks + devlinks_len++, " "); - e = stpcpy(devlinks + devlinks_len, devlink); - devlinks_len = e - devlinks; - } - - r = device_add_property_internal(device, "DEVLINKS", devlinks); - if (r < 0) - return r; - - device->property_devlinks_outdated = false; - } - - if (device->property_tags_outdated) { - _cleanup_free_ char *tags = NULL; - size_t tags_allocated = 0, tags_len = 0; - const char *tag; - - if (!GREEDY_REALLOC(tags, tags_allocated, 2)) - return -ENOMEM; - stpcpy(tags, ":"); - tags_len++; - - for (tag = sd_device_get_tag_first(device); tag; tag = sd_device_get_tag_next(device)) { - char *e; - - if (!GREEDY_REALLOC(tags, tags_allocated, tags_len + strlen(tag) + 2)) - return -ENOMEM; - e = stpcpy(stpcpy(tags + tags_len, tag), ":"); - tags_len = e - tags; - } - - r = device_add_property_internal(device, "TAGS", tags); - if (r < 0) - return r; - - device->property_tags_outdated = false; - } - - return 0; -} - -_public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) { - const char *key; - const char *value; - int r; - - assert_return(device, NULL); - - r = device_properties_prepare(device); - if (r < 0) - return NULL; - - device->properties_iterator_generation = device->properties_generation; - device->properties_iterator = ITERATOR_FIRST; - - ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)&value, (const void**)&key); - - if (_value) - *_value = value; - - return key; -} - -_public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) { - const char *key; - const char *value; - int r; - - assert_return(device, NULL); - - r = device_properties_prepare(device); - if (r < 0) - return NULL; - - if (device->properties_iterator_generation != device->properties_generation) - return NULL; - - ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)&value, (const void**)&key); - - if (_value) - *_value = value; - - return key; -} - -static int device_sysattrs_read_all(sd_device *device) { - _cleanup_closedir_ DIR *dir = NULL; - const char *syspath; - struct dirent *dent; - int r; - - assert(device); - - if (device->sysattrs_read) - return 0; - - r = sd_device_get_syspath(device, &syspath); - if (r < 0) - return r; - - dir = opendir(syspath); - if (!dir) - return -errno; - - r = set_ensure_allocated(&device->sysattrs, &string_hash_ops); - if (r < 0) - return r; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char *path; - struct stat statbuf; - - /* only handle symlinks and regular files */ - if (dent->d_type != DT_LNK && dent->d_type != DT_REG) - continue; - - path = strjoina(syspath, "/", dent->d_name); - - if (lstat(path, &statbuf) != 0) - continue; - - if (!(statbuf.st_mode & S_IRUSR)) - continue; - - r = set_put_strdup(device->sysattrs, dent->d_name); - if (r < 0) - return r; - } - - device->sysattrs_read = true; - - return 0; -} - -_public_ const char *sd_device_get_sysattr_first(sd_device *device) { - void *v; - int r; - - assert_return(device, NULL); - - if (!device->sysattrs_read) { - r = device_sysattrs_read_all(device); - if (r < 0) { - errno = -r; - return NULL; - } - } - - device->sysattrs_iterator = ITERATOR_FIRST; - - (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); - return v; -} - -_public_ const char *sd_device_get_sysattr_next(sd_device *device) { - void *v; - - assert_return(device, NULL); - - if (!device->sysattrs_read) - return NULL; - - (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); - return v; -} - -_public_ int sd_device_has_tag(sd_device *device, const char *tag) { - assert_return(device, -EINVAL); - assert_return(tag, -EINVAL); - - (void) device_read_db(device); - - return !!set_contains(device->tags, tag); -} - -_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) { - char *value; - int r; - - assert_return(device, -EINVAL); - assert_return(key, -EINVAL); - assert_return(_value, -EINVAL); - - r = device_properties_prepare(device); - if (r < 0) - return r; - - value = ordered_hashmap_get(device->properties, key); - if (!value) - return -ENOENT; - - *_value = value; - - return 0; -} - -/* replaces the value if it already exists */ -static int device_add_sysattr_value(sd_device *device, const char *_key, char *value) { - _cleanup_free_ char *key = NULL; - _cleanup_free_ char *value_old = NULL; - int r; - - assert(device); - assert(_key); - - r = hashmap_ensure_allocated(&device->sysattr_values, &string_hash_ops); - if (r < 0) - return r; - - value_old = hashmap_remove2(device->sysattr_values, _key, (void **)&key); - if (!key) { - key = strdup(_key); - if (!key) - return -ENOMEM; - } - - r = hashmap_put(device->sysattr_values, key, value); - if (r < 0) - return r; - - key = NULL; - - return 0; -} - -static int device_get_sysattr_value(sd_device *device, const char *_key, const char **_value) { - const char *key = NULL, *value; - - assert(device); - assert(_key); - - value = hashmap_get2(device->sysattr_values, _key, (void **) &key); - if (!key) - return -ENOENT; - - if (_value) - *_value = value; - - return 0; -} - -/* We cache all sysattr lookups. If an attribute does not exist, it is stored - * with a NULL value in the cache, otherwise the returned string is stored */ -_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) { - _cleanup_free_ char *value = NULL; - const char *syspath, *cached_value = NULL; - char *path; - struct stat statbuf; - int r; - - assert_return(device, -EINVAL); - assert_return(sysattr, -EINVAL); - - /* look for possibly already cached result */ - r = device_get_sysattr_value(device, sysattr, &cached_value); - if (r != -ENOENT) { - if (r < 0) - return r; - - if (!cached_value) - /* we looked up the sysattr before and it did not exist */ - return -ENOENT; - - if (_value) - *_value = cached_value; - - return 0; - } - - r = sd_device_get_syspath(device, &syspath); - if (r < 0) - return r; - - path = strjoina(syspath, "/", sysattr); - r = lstat(path, &statbuf); - if (r < 0) { - /* remember that we could not access the sysattr */ - r = device_add_sysattr_value(device, sysattr, NULL); - if (r < 0) - return r; - - return -ENOENT; - } else if (S_ISLNK(statbuf.st_mode)) { - /* Some core links return only the last element of the target path, - * these are just values, the paths should not be exposed. */ - if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) { - r = readlink_value(path, &value); - if (r < 0) - return r; - } else - return -EINVAL; - } else if (S_ISDIR(statbuf.st_mode)) { - /* skip directories */ - return -EINVAL; - } else if (!(statbuf.st_mode & S_IRUSR)) { - /* skip non-readable files */ - return -EPERM; - } else { - size_t size; - - /* read attribute value */ - r = read_full_file(path, &value, &size); - if (r < 0) - return r; - - /* drop trailing newlines */ - while (size > 0 && value[--size] == '\n') - value[size] = '\0'; - } - - r = device_add_sysattr_value(device, sysattr, value); - if (r < 0) - return r; - - *_value = value; - value = NULL; - - return 0; -} - -static void device_remove_sysattr_value(sd_device *device, const char *_key) { - _cleanup_free_ char *key = NULL; - _cleanup_free_ char *value = NULL; - - assert(device); - assert(_key); - - value = hashmap_remove2(device->sysattr_values, _key, (void **) &key); - - return; -} - -/* set the attribute and save it in the cache. If a NULL value is passed the - * attribute is cleared from the cache */ -_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *_value) { - _cleanup_close_ int fd = -1; - _cleanup_free_ char *value = NULL; - const char *syspath; - char *path; - struct stat statbuf; - size_t value_len = 0; - ssize_t size; - int r; - - assert_return(device, -EINVAL); - assert_return(sysattr, -EINVAL); - - if (!_value) { - device_remove_sysattr_value(device, sysattr); - - return 0; - } - - r = sd_device_get_syspath(device, &syspath); - if (r < 0) - return r; - - path = strjoina(syspath, "/", sysattr); - r = lstat(path, &statbuf); - if (r < 0) { - value = strdup(""); - if (!value) - return -ENOMEM; - - r = device_add_sysattr_value(device, sysattr, value); - if (r < 0) - return r; - - return -ENXIO; - } - - if (S_ISLNK(statbuf.st_mode)) - return -EINVAL; - - /* skip directories */ - if (S_ISDIR(statbuf.st_mode)) - return -EISDIR; - - /* skip non-readable files */ - if ((statbuf.st_mode & S_IRUSR) == 0) - return -EACCES; - - value_len = strlen(_value); - - /* drop trailing newlines */ - while (value_len > 0 && _value[value_len - 1] == '\n') - _value[--value_len] = '\0'; - - /* value length is limited to 4k */ - if (value_len > 4096) - return -EINVAL; - - fd = open(path, O_WRONLY | O_CLOEXEC); - if (fd < 0) - return -errno; - - value = strdup(_value); - if (!value) - return -ENOMEM; - - size = write(fd, value, value_len); - if (size < 0) - return -errno; - - if ((size_t)size != value_len) - return -EIO; - - r = device_add_sysattr_value(device, sysattr, value); - if (r < 0) - return r; - - value = NULL; - - return 0; -} diff --git a/src/libsystemd/sd-event/Makefile b/src/libsystemd/sd-event/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-event/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c deleted file mode 100644 index 7ba6527f63..0000000000 --- a/src/libsystemd/sd-event/sd-event.c +++ /dev/null @@ -1,2898 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include - -#include "sd-daemon.h" -#include "sd-event.h" -#include "sd-id128.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "hashmap.h" -#include "list.h" -#include "macro.h" -#include "missing.h" -#include "prioq.h" -#include "process-util.h" -#include "set.h" -#include "signal-util.h" -#include "string-table.h" -#include "string-util.h" -#include "time-util.h" -#include "util.h" - -#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC) - -typedef enum EventSourceType { - SOURCE_IO, - SOURCE_TIME_REALTIME, - SOURCE_TIME_BOOTTIME, - SOURCE_TIME_MONOTONIC, - SOURCE_TIME_REALTIME_ALARM, - SOURCE_TIME_BOOTTIME_ALARM, - SOURCE_SIGNAL, - SOURCE_CHILD, - SOURCE_DEFER, - SOURCE_POST, - SOURCE_EXIT, - SOURCE_WATCHDOG, - _SOURCE_EVENT_SOURCE_TYPE_MAX, - _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1 -} EventSourceType; - -static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = { - [SOURCE_IO] = "io", - [SOURCE_TIME_REALTIME] = "realtime", - [SOURCE_TIME_BOOTTIME] = "bootime", - [SOURCE_TIME_MONOTONIC] = "monotonic", - [SOURCE_TIME_REALTIME_ALARM] = "realtime-alarm", - [SOURCE_TIME_BOOTTIME_ALARM] = "boottime-alarm", - [SOURCE_SIGNAL] = "signal", - [SOURCE_CHILD] = "child", - [SOURCE_DEFER] = "defer", - [SOURCE_POST] = "post", - [SOURCE_EXIT] = "exit", - [SOURCE_WATCHDOG] = "watchdog", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int); - -/* All objects we use in epoll events start with this value, so that - * we know how to dispatch it */ -typedef enum WakeupType { - WAKEUP_NONE, - WAKEUP_EVENT_SOURCE, - WAKEUP_CLOCK_DATA, - WAKEUP_SIGNAL_DATA, - _WAKEUP_TYPE_MAX, - _WAKEUP_TYPE_INVALID = -1, -} WakeupType; - -#define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM) - -struct sd_event_source { - WakeupType wakeup; - - unsigned n_ref; - - sd_event *event; - void *userdata; - sd_event_handler_t prepare; - - char *description; - - EventSourceType type:5; - int enabled:3; - bool pending:1; - bool dispatching:1; - bool floating:1; - - int64_t priority; - unsigned pending_index; - unsigned prepare_index; - unsigned pending_iteration; - unsigned prepare_iteration; - - LIST_FIELDS(sd_event_source, sources); - - 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; - } post; - struct { - sd_event_handler_t callback; - unsigned prioq_index; - } exit; - }; -}; - -struct clock_data { - WakeupType wakeup; - int fd; - - /* For all 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 *earliest; - Prioq *latest; - usec_t next; - - bool needs_rearm:1; -}; - -struct signal_data { - WakeupType wakeup; - - /* For each priority we maintain one signal fd, so that we - * only have to dequeue a single event per priority at a - * time. */ - - int fd; - int64_t priority; - sigset_t sigset; - sd_event_source *current; -}; - -struct sd_event { - unsigned n_ref; - - int epoll_fd; - int watchdog_fd; - - Prioq *pending; - Prioq *prepare; - - /* timerfd_create() only supports these five clocks so far. We - * can add support for more clocks when the kernel learns to - * deal with them, too. */ - struct clock_data realtime; - struct clock_data boottime; - struct clock_data monotonic; - struct clock_data realtime_alarm; - struct clock_data boottime_alarm; - - usec_t perturb; - - sd_event_source **signal_sources; /* indexed by signal number */ - Hashmap *signal_data; /* indexed by priority */ - - Hashmap *child_sources; - unsigned n_enabled_child_sources; - - Set *post_sources; - - Prioq *exit; - - pid_t original_pid; - - unsigned iteration; - dual_timestamp timestamp; - usec_t timestamp_boottime; - int state; - - bool exit_requested:1; - bool need_process_child:1; - bool watchdog:1; - bool profile_delays:1; - - int exit_code; - - pid_t tid; - sd_event **default_event_ptr; - - usec_t watchdog_last, watchdog_period; - - unsigned n_sources; - - LIST_HEAD(sd_event_source, sources); - - usec_t last_run, last_log; - unsigned delays[sizeof(usec_t) * 8]; -}; - -static void source_disconnect(sd_event_source *s); - -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; - - 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); - - /* 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 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; - - /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - 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(EVENT_SOURCE_IS_TIME(x->type)); - assert(x->type == y->type); - - /* 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; - - return 0; -} - -static usec_t time_event_source_latest(const sd_event_source *s) { - return usec_add(s->time.next, s->time.accuracy); -} - -static int latest_time_prioq_compare(const void *a, const void *b) { - const sd_event_source *x = a, *y = b; - - assert(EVENT_SOURCE_IS_TIME(x->type)); - assert(x->type == y->type); - - /* 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 (time_event_source_latest(x) < time_event_source_latest(y)) - return -1; - if (time_event_source_latest(x) > time_event_source_latest(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; - - return 0; -} - -static void free_clock_data(struct clock_data *d) { - assert(d); - assert(d->wakeup == WAKEUP_CLOCK_DATA); - - safe_close(d->fd); - prioq_free(d->earliest); - prioq_free(d->latest); -} - -static void event_free(sd_event *e) { - sd_event_source *s; - - assert(e); - - while ((s = e->sources)) { - assert(s->floating); - source_disconnect(s); - sd_event_source_unref(s); - } - - assert(e->n_sources == 0); - - if (e->default_event_ptr) - *(e->default_event_ptr) = NULL; - - safe_close(e->epoll_fd); - safe_close(e->watchdog_fd); - - free_clock_data(&e->realtime); - free_clock_data(&e->boottime); - free_clock_data(&e->monotonic); - free_clock_data(&e->realtime_alarm); - free_clock_data(&e->boottime_alarm); - - prioq_free(e->pending); - prioq_free(e->prepare); - prioq_free(e->exit); - - free(e->signal_sources); - hashmap_free(e->signal_data); - - hashmap_free(e->child_sources); - set_free(e->post_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->watchdog_fd = e->epoll_fd = e->realtime.fd = e->boottime.fd = e->monotonic.fd = e->realtime_alarm.fd = e->boottime_alarm.fd = -1; - e->realtime.next = e->boottime.next = e->monotonic.next = e->realtime_alarm.next = e->boottime_alarm.next = USEC_INFINITY; - e->realtime.wakeup = e->boottime.wakeup = e->monotonic.wakeup = e->realtime_alarm.wakeup = e->boottime_alarm.wakeup = WAKEUP_CLOCK_DATA; - e->original_pid = getpid(); - e->perturb = USEC_INFINITY; - - r = prioq_ensure_allocated(&e->pending, pending_prioq_compare); - if (r < 0) - goto fail; - - e->epoll_fd = epoll_create1(EPOLL_CLOEXEC); - if (e->epoll_fd < 0) { - r = -errno; - goto fail; - } - - if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) { - log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s."); - e->profile_delays = true; - } - - *ret = e; - return 0; - -fail: - event_free(e); - return r; -} - -_public_ sd_event* sd_event_ref(sd_event *e) { - - if (!e) - return 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 an event loop and keeping - * it around over a fork(). Let's complain. */ - - return e->original_pid != getpid(); -} - -static void source_io_unregister(sd_event_source *s) { - int r; - - assert(s); - assert(s->type == SOURCE_IO); - - if (event_pid_changed(s->event)) - return; - - if (!s->io.registered) - return; - - r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL); - if (r < 0) - log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll: %m", - strna(s->description), event_source_type_to_string(s->type)); - - s->io.registered = false; -} - -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 clockid_t event_source_type_to_clock(EventSourceType t) { - - switch (t) { - - case SOURCE_TIME_REALTIME: - return CLOCK_REALTIME; - - case SOURCE_TIME_BOOTTIME: - return CLOCK_BOOTTIME; - - case SOURCE_TIME_MONOTONIC: - return CLOCK_MONOTONIC; - - case SOURCE_TIME_REALTIME_ALARM: - return CLOCK_REALTIME_ALARM; - - case SOURCE_TIME_BOOTTIME_ALARM: - return CLOCK_BOOTTIME_ALARM; - - default: - return (clockid_t) -1; - } -} - -static EventSourceType clock_to_event_source_type(clockid_t clock) { - - switch (clock) { - - case CLOCK_REALTIME: - return SOURCE_TIME_REALTIME; - - case CLOCK_BOOTTIME: - return SOURCE_TIME_BOOTTIME; - - case CLOCK_MONOTONIC: - return SOURCE_TIME_MONOTONIC; - - case CLOCK_REALTIME_ALARM: - return SOURCE_TIME_REALTIME_ALARM; - - case CLOCK_BOOTTIME_ALARM: - return SOURCE_TIME_BOOTTIME_ALARM; - - default: - return _SOURCE_EVENT_SOURCE_TYPE_INVALID; - } -} - -static struct clock_data* event_get_clock_data(sd_event *e, EventSourceType t) { - assert(e); - - switch (t) { - - case SOURCE_TIME_REALTIME: - return &e->realtime; - - case SOURCE_TIME_BOOTTIME: - return &e->boottime; - - case SOURCE_TIME_MONOTONIC: - return &e->monotonic; - - case SOURCE_TIME_REALTIME_ALARM: - return &e->realtime_alarm; - - case SOURCE_TIME_BOOTTIME_ALARM: - return &e->boottime_alarm; - - default: - return NULL; - } -} - -static int event_make_signal_data( - sd_event *e, - int sig, - struct signal_data **ret) { - - struct epoll_event ev = {}; - struct signal_data *d; - bool added = false; - sigset_t ss_copy; - int64_t priority; - int r; - - assert(e); - - if (event_pid_changed(e)) - return -ECHILD; - - if (e->signal_sources && e->signal_sources[sig]) - priority = e->signal_sources[sig]->priority; - else - priority = 0; - - d = hashmap_get(e->signal_data, &priority); - if (d) { - if (sigismember(&d->sigset, sig) > 0) { - if (ret) - *ret = d; - return 0; - } - } else { - r = hashmap_ensure_allocated(&e->signal_data, &uint64_hash_ops); - if (r < 0) - return r; - - d = new0(struct signal_data, 1); - if (!d) - return -ENOMEM; - - d->wakeup = WAKEUP_SIGNAL_DATA; - d->fd = -1; - d->priority = priority; - - r = hashmap_put(e->signal_data, &d->priority, d); - if (r < 0) { - free(d); - return r; - } - - added = true; - } - - ss_copy = d->sigset; - assert_se(sigaddset(&ss_copy, sig) >= 0); - - r = signalfd(d->fd, &ss_copy, SFD_NONBLOCK|SFD_CLOEXEC); - if (r < 0) { - r = -errno; - goto fail; - } - - d->sigset = ss_copy; - - if (d->fd >= 0) { - if (ret) - *ret = d; - return 0; - } - - d->fd = r; - - ev.events = EPOLLIN; - ev.data.ptr = d; - - r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, d->fd, &ev); - if (r < 0) { - r = -errno; - goto fail; - } - - if (ret) - *ret = d; - - return 0; - -fail: - if (added) { - d->fd = safe_close(d->fd); - hashmap_remove(e->signal_data, &d->priority); - free(d); - } - - return r; -} - -static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig) { - assert(e); - assert(d); - - /* Turns off the specified signal in the signal data - * object. If the signal mask of the object becomes empty that - * way removes it. */ - - if (sigismember(&d->sigset, sig) == 0) - return; - - assert_se(sigdelset(&d->sigset, sig) >= 0); - - if (sigisemptyset(&d->sigset)) { - - /* If all the mask is all-zero we can get rid of the structure */ - hashmap_remove(e->signal_data, &d->priority); - assert(!d->current); - safe_close(d->fd); - free(d); - return; - } - - assert(d->fd >= 0); - - if (signalfd(d->fd, &d->sigset, SFD_NONBLOCK|SFD_CLOEXEC) < 0) - log_debug_errno(errno, "Failed to unset signal bit, ignoring: %m"); -} - -static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig) { - struct signal_data *d; - static const int64_t zero_priority = 0; - - assert(e); - - /* Rechecks if the specified signal is still something we are - * interested in. If not, we'll unmask it, and possibly drop - * the signalfd for it. */ - - if (sig == SIGCHLD && - e->n_enabled_child_sources > 0) - return; - - if (e->signal_sources && - e->signal_sources[sig] && - e->signal_sources[sig]->enabled != SD_EVENT_OFF) - return; - - /* - * The specified signal might be enabled in three different queues: - * - * 1) the one that belongs to the priority passed (if it is non-NULL) - * 2) the one that belongs to the priority of the event source of the signal (if there is one) - * 3) the 0 priority (to cover the SIGCHLD case) - * - * Hence, let's remove it from all three here. - */ - - if (priority) { - d = hashmap_get(e->signal_data, priority); - if (d) - event_unmask_signal_data(e, d, sig); - } - - if (e->signal_sources && e->signal_sources[sig]) { - d = hashmap_get(e->signal_data, &e->signal_sources[sig]->priority); - if (d) - event_unmask_signal_data(e, d, sig); - } - - d = hashmap_get(e->signal_data, &zero_priority); - if (d) - event_unmask_signal_data(e, d, sig); -} - -static void source_disconnect(sd_event_source *s) { - sd_event *event; - - assert(s); - - if (!s->event) - return; - - assert(s->event->n_sources > 0); - - switch (s->type) { - - case SOURCE_IO: - if (s->io.fd >= 0) - source_io_unregister(s); - - break; - - case SOURCE_TIME_REALTIME: - case SOURCE_TIME_BOOTTIME: - case SOURCE_TIME_MONOTONIC: - case SOURCE_TIME_REALTIME_ALARM: - case SOURCE_TIME_BOOTTIME_ALARM: { - struct clock_data *d; - - d = event_get_clock_data(s->event, s->type); - assert(d); - - prioq_remove(d->earliest, s, &s->time.earliest_index); - prioq_remove(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - break; - } - - case SOURCE_SIGNAL: - if (s->signal.sig > 0) { - - if (s->event->signal_sources) - s->event->signal_sources[s->signal.sig] = NULL; - - event_gc_signal_data(s->event, &s->priority, s->signal.sig); - } - - 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--; - } - - (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid)); - event_gc_signal_data(s->event, &s->priority, SIGCHLD); - } - - break; - - case SOURCE_DEFER: - /* nothing */ - break; - - case SOURCE_POST: - set_remove(s->event->post_sources, s); - break; - - case SOURCE_EXIT: - prioq_remove(s->event->exit, s, &s->exit.prioq_index); - break; - - default: - 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); - - event = s->event; - - s->type = _SOURCE_EVENT_SOURCE_TYPE_INVALID; - s->event = NULL; - LIST_REMOVE(sources, event->sources, s); - event->n_sources--; - - if (!s->floating) - sd_event_unref(event); -} - -static void source_free(sd_event_source *s) { - assert(s); - - source_disconnect(s); - free(s->description); - 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 (EVENT_SOURCE_IS_TIME(s->type)) { - struct clock_data *d; - - d = event_get_clock_data(s->event, s->type); - assert(d); - - prioq_reshuffle(d->earliest, s, &s->time.earliest_index); - prioq_reshuffle(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - } - - if (s->type == SOURCE_SIGNAL && !b) { - struct signal_data *d; - - d = hashmap_get(s->event->signal_data, &s->priority); - if (d && d->current == s) - d->current = NULL; - } - - return 0; -} - -static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType type) { - sd_event_source *s; - - assert(e); - - s = new0(sd_event_source, 1); - if (!s) - return NULL; - - s->n_ref = 1; - s->event = e; - s->floating = floating; - s->type = type; - s->pending_index = s->prepare_index = PRIOQ_IDX_NULL; - - if (!floating) - sd_event_ref(e); - - LIST_PREPEND(sources, e->sources, s); - e->n_sources++; - - return s; -} - -_public_ int sd_event_add_io( - sd_event *e, - sd_event_source **ret, - int fd, - uint32_t events, - sd_event_io_handler_t callback, - void *userdata) { - - sd_event_source *s; - int r; - - assert_return(e, -EINVAL); - assert_return(fd >= 0, -EBADF); - assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL); - assert_return(callback, -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - s = source_new(e, !ret, SOURCE_IO); - if (!s) - return -ENOMEM; - - s->wakeup = WAKEUP_EVENT_SOURCE; - 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 r; - } - - if (ret) - *ret = s; - - return 0; -} - -static void initialize_perturb(sd_event *e) { - sd_id128_t bootid = {}; - - /* 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 (_likely_(e->perturb != USEC_INFINITY)) - return; - - if (sd_id128_get_boot(&bootid) >= 0) - e->perturb = (bootid.qwords[0] ^ bootid.qwords[1]) % USEC_PER_MINUTE; -} - -static int event_setup_timer_fd( - sd_event *e, - struct clock_data *d, - clockid_t clock) { - - struct epoll_event ev = {}; - int r, fd; - - assert(e); - assert(d); - - if (_likely_(d->fd >= 0)) - return 0; - - fd = timerfd_create(clock, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - return -errno; - - ev.events = EPOLLIN; - ev.data.ptr = d; - - r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, fd, &ev); - if (r < 0) { - safe_close(fd); - return -errno; - } - - d->fd = fd; - return 0; -} - -static int time_exit_callback(sd_event_source *s, uint64_t usec, void *userdata) { - assert(s); - - return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata)); -} - -_public_ int sd_event_add_time( - sd_event *e, - sd_event_source **ret, - clockid_t clock, - uint64_t usec, - uint64_t accuracy, - sd_event_time_handler_t callback, - void *userdata) { - - EventSourceType type; - sd_event_source *s; - struct clock_data *d; - int r; - - assert_return(e, -EINVAL); - assert_return(accuracy != (uint64_t) -1, -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && - !clock_boottime_supported()) - return -EOPNOTSUPP; - - if (!callback) - callback = time_exit_callback; - - type = clock_to_event_source_type(clock); - assert_return(type >= 0, -EOPNOTSUPP); - - d = event_get_clock_data(e, type); - assert(d); - - r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare); - if (r < 0) - return r; - - r = prioq_ensure_allocated(&d->latest, latest_time_prioq_compare); - if (r < 0) - return r; - - if (d->fd < 0) { - r = event_setup_timer_fd(e, d, clock); - if (r < 0) - return r; - } - - s = source_new(e, !ret, 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; - - d->needs_rearm = true; - - r = prioq_put(d->earliest, s, &s->time.earliest_index); - if (r < 0) - goto fail; - - r = prioq_put(d->latest, s, &s->time.latest_index); - if (r < 0) - goto fail; - - if (ret) - *ret = s; - - return 0; - -fail: - source_free(s); - return r; -} - -static int signal_exit_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - assert(s); - - return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata)); -} - -_public_ int sd_event_add_signal( - sd_event *e, - sd_event_source **ret, - int sig, - sd_event_signal_handler_t callback, - void *userdata) { - - sd_event_source *s; - struct signal_data *d; - sigset_t ss; - int r; - - assert_return(e, -EINVAL); - assert_return(SIGNAL_VALID(sig), -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - if (!callback) - callback = signal_exit_callback; - - r = pthread_sigmask(SIG_SETMASK, NULL, &ss); - if (r != 0) - return -r; - - 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, !ret, 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; - - r = event_make_signal_data(e, sig, &d); - if (r < 0) { - source_free(s); - return r; - } - - /* Use the signal name as description for the event source by default */ - (void) sd_event_source_set_description(s, signal_to_string(sig)); - - if (ret) - *ret = s; - - return 0; -} - -_public_ int sd_event_add_child( - sd_event *e, - sd_event_source **ret, - pid_t pid, - int options, - sd_event_child_handler_t callback, - void *userdata) { - - 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(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - r = hashmap_ensure_allocated(&e->child_sources, NULL); - if (r < 0) - return r; - - if (hashmap_contains(e->child_sources, PID_TO_PTR(pid))) - return -EBUSY; - - s = source_new(e, !ret, 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, PID_TO_PTR(pid), s); - if (r < 0) { - source_free(s); - return r; - } - - e->n_enabled_child_sources++; - - r = event_make_signal_data(e, SIGCHLD, NULL); - if (r < 0) { - e->n_enabled_child_sources--; - source_free(s); - return r; - } - - e->need_process_child = true; - - if (ret) - *ret = s; - - return 0; -} - -_public_ int sd_event_add_defer( - sd_event *e, - sd_event_source **ret, - sd_event_handler_t callback, - void *userdata) { - - sd_event_source *s; - int r; - - assert_return(e, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - s = source_new(e, !ret, 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; - } - - if (ret) - *ret = s; - - return 0; -} - -_public_ int sd_event_add_post( - sd_event *e, - sd_event_source **ret, - sd_event_handler_t callback, - void *userdata) { - - sd_event_source *s; - int r; - - assert_return(e, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - r = set_ensure_allocated(&e->post_sources, NULL); - if (r < 0) - return r; - - s = source_new(e, !ret, SOURCE_POST); - if (!s) - return -ENOMEM; - - s->post.callback = callback; - s->userdata = userdata; - s->enabled = SD_EVENT_ON; - - r = set_put(e->post_sources, s); - if (r < 0) { - source_free(s); - return r; - } - - if (ret) - *ret = s; - - return 0; -} - -_public_ int sd_event_add_exit( - sd_event *e, - sd_event_source **ret, - sd_event_handler_t callback, - void *userdata) { - - sd_event_source *s; - int r; - - assert_return(e, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - r = prioq_ensure_allocated(&e->exit, exit_prioq_compare); - if (r < 0) - return r; - - s = source_new(e, !ret, 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; - } - - if (ret) - *ret = s; - - return 0; -} - -_public_ sd_event_source* sd_event_source_ref(sd_event_source *s) { - - if (!s) - return 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); - - source_disconnect(s); - } else - source_free(s); - } - - return NULL; -} - -_public_ int sd_event_source_set_description(sd_event_source *s, const char *description) { - assert_return(s, -EINVAL); - assert_return(!event_pid_changed(s->event), -ECHILD); - - return free_and_strdup(&s->description, description); -} - -_public_ int sd_event_source_get_description(sd_event_source *s, const char **description) { - assert_return(s, -EINVAL); - assert_return(description, -EINVAL); - assert_return(s->description, -ENXIO); - assert_return(!event_pid_changed(s->event), -ECHILD); - - *description = s->description; - return 0; -} - -_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, -EBADF); - 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); - - /* edge-triggered updates are never skipped, so we can reset edges */ - if (s->io.events == events && !(events & EPOLLET)) - 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) { - int r; - - 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; - - if (s->type == SOURCE_SIGNAL && s->enabled != SD_EVENT_OFF) { - struct signal_data *old, *d; - - /* Move us from the signalfd belonging to the old - * priority to the signalfd of the new priority */ - - assert_se(old = hashmap_get(s->event->signal_data, &s->priority)); - - s->priority = priority; - - r = event_make_signal_data(s->event, s->signal.sig, &d); - if (r < 0) { - s->priority = old->priority; - return r; - } - - event_unmask_signal_data(s->event, old, s->signal.sig); - } else - 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(!event_pid_changed(s->event), -ECHILD); - - /* If we are dead anyway, we are fine with turning off - * sources, but everything else needs to fail. */ - if (s->event->state == SD_EVENT_FINISHED) - return m == SD_EVENT_OFF ? 0 : -ESTALE; - - if (s->enabled == m) - return 0; - - if (m == SD_EVENT_OFF) { - - switch (s->type) { - - case SOURCE_IO: - source_io_unregister(s); - s->enabled = m; - break; - - case SOURCE_TIME_REALTIME: - case SOURCE_TIME_BOOTTIME: - case SOURCE_TIME_MONOTONIC: - case SOURCE_TIME_REALTIME_ALARM: - case SOURCE_TIME_BOOTTIME_ALARM: { - struct clock_data *d; - - s->enabled = m; - d = event_get_clock_data(s->event, s->type); - assert(d); - - prioq_reshuffle(d->earliest, s, &s->time.earliest_index); - prioq_reshuffle(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - break; - } - - case SOURCE_SIGNAL: - s->enabled = m; - - event_gc_signal_data(s->event, &s->priority, s->signal.sig); - break; - - case SOURCE_CHILD: - s->enabled = m; - - assert(s->event->n_enabled_child_sources > 0); - s->event->n_enabled_child_sources--; - - event_gc_signal_data(s->event, &s->priority, SIGCHLD); - break; - - case SOURCE_EXIT: - s->enabled = m; - prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); - break; - - case SOURCE_DEFER: - case SOURCE_POST: - s->enabled = m; - break; - - default: - 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_TIME_REALTIME: - case SOURCE_TIME_BOOTTIME: - case SOURCE_TIME_MONOTONIC: - case SOURCE_TIME_REALTIME_ALARM: - case SOURCE_TIME_BOOTTIME_ALARM: { - struct clock_data *d; - - s->enabled = m; - d = event_get_clock_data(s->event, s->type); - assert(d); - - prioq_reshuffle(d->earliest, s, &s->time.earliest_index); - prioq_reshuffle(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - break; - } - - case SOURCE_SIGNAL: - - s->enabled = m; - - r = event_make_signal_data(s->event, s->signal.sig, NULL); - if (r < 0) { - s->enabled = SD_EVENT_OFF; - event_gc_signal_data(s->event, &s->priority, s->signal.sig); - return r; - } - - break; - - case SOURCE_CHILD: - - if (s->enabled == SD_EVENT_OFF) - s->event->n_enabled_child_sources++; - - s->enabled = m; - - r = event_make_signal_data(s->event, SIGCHLD, NULL); - if (r < 0) { - s->enabled = SD_EVENT_OFF; - s->event->n_enabled_child_sources--; - event_gc_signal_data(s->event, &s->priority, SIGCHLD); - return r; - } - - break; - - case SOURCE_EXIT: - s->enabled = m; - prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); - break; - - case SOURCE_DEFER: - case SOURCE_POST: - s->enabled = m; - break; - - default: - 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(EVENT_SOURCE_IS_TIME(s->type), -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) { - struct clock_data *d; - - assert_return(s, -EINVAL); - assert_return(EVENT_SOURCE_IS_TIME(s->type), -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); - - d = event_get_clock_data(s->event, s->type); - assert(d); - - prioq_reshuffle(d->earliest, s, &s->time.earliest_index); - prioq_reshuffle(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - - 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(EVENT_SOURCE_IS_TIME(s->type), -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) { - struct clock_data *d; - - assert_return(s, -EINVAL); - assert_return(usec != (uint64_t) -1, -EINVAL); - assert_return(EVENT_SOURCE_IS_TIME(s->type), -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); - - d = event_get_clock_data(s->event, s->type); - assert(d); - - prioq_reshuffle(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - - return 0; -} - -_public_ int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock) { - assert_return(s, -EINVAL); - assert_return(clock, -EINVAL); - assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); - - *clock = event_source_type_to_clock(s->type); - 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 (a >= USEC_INFINITY) - return USEC_INFINITY; - - if (b <= a + 1) - return a; - - initialize_perturb(e); - - /* - 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, - struct clock_data *d) { - - struct itimerspec its = {}; - sd_event_source *a, *b; - usec_t t; - int r; - - assert(e); - assert(d); - - if (!d->needs_rearm) - return 0; - else - d->needs_rearm = false; - - a = prioq_peek(d->earliest); - if (!a || a->enabled == SD_EVENT_OFF || a->time.next == USEC_INFINITY) { - - if (d->fd < 0) - return 0; - - if (d->next == USEC_INFINITY) - return 0; - - /* disarm */ - r = timerfd_settime(d->fd, TFD_TIMER_ABSTIME, &its, NULL); - if (r < 0) - return r; - - d->next = USEC_INFINITY; - return 0; - } - - b = prioq_peek(d->latest); - assert_se(b && b->enabled != SD_EVENT_OFF); - - t = sleep_between(e, a->time.next, time_event_source_latest(b)); - if (d->next == t) - return 0; - - assert_se(d->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(d->fd, TFD_TIMER_ABSTIME, &its, NULL); - if (r < 0) - return -errno; - - d->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_INFINITY; - - return 0; -} - -static int process_timer( - sd_event *e, - usec_t n, - struct clock_data *d) { - - sd_event_source *s; - int r; - - assert(e); - assert(d); - - for (;;) { - s = prioq_peek(d->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(d->earliest, s, &s->time.earliest_index); - prioq_reshuffle(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - } - - 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, struct signal_data *d, uint32_t events) { - bool read_one = false; - int r; - - assert(e); - assert_return(events == EPOLLIN, -EIO); - - /* If there's a signal queued on this priority and SIGCHLD is - on this priority too, then make sure to recheck the - children we watch. This is because we only ever dequeue - the first signal per priority, and if we dequeue one, and - SIGCHLD might be enqueued later we wouldn't know, but we - might have higher priority children we care about hence we - need to check that explicitly. */ - - if (sigismember(&d->sigset, SIGCHLD)) - e->need_process_child = true; - - /* If there's already an event source pending for this - * priority we don't read another */ - if (d->current) - return 0; - - for (;;) { - struct signalfd_siginfo si; - ssize_t n; - sd_event_source *s = NULL; - - n = read(d->fd, &si, sizeof(si)); - if (n < 0) { - if (errno == EAGAIN || errno == EINTR) - return read_one; - - return -errno; - } - - if (_unlikely_(n != sizeof(si))) - return -EIO; - - assert(SIGNAL_VALID(si.ssi_signo)); - - read_one = true; - - if (e->signal_sources) - s = e->signal_sources[si.ssi_signo]; - if (!s) - continue; - if (s->pending) - continue; - - s->signal.siginfo = si; - d->current = s; - - r = source_set_pending(s, true); - if (r < 0) - return r; - - return 1; - } -} - -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->type != SOURCE_POST) { - sd_event_source *z; - Iterator i; - - /* If we execute a non-post source, let's mark all - * post sources as pending */ - - SET_FOREACH(z, s->event->post_sources, i) { - if (z->enabled == SD_EVENT_OFF) - continue; - - r = source_set_pending(z, true); - 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_TIME_REALTIME: - case SOURCE_TIME_BOOTTIME: - case SOURCE_TIME_MONOTONIC: - case SOURCE_TIME_REALTIME_ALARM: - case SOURCE_TIME_BOOTTIME_ALARM: - 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_POST: - r = s->post.callback(s, s->userdata); - break; - - case SOURCE_EXIT: - r = s->exit.callback(s, s->userdata); - break; - - case SOURCE_WATCHDOG: - case _SOURCE_EVENT_SOURCE_TYPE_MAX: - case _SOURCE_EVENT_SOURCE_TYPE_INVALID: - assert_not_reached("Wut? I shouldn't exist."); - } - - s->dispatching = false; - - if (r < 0) - log_debug_errno(r, "Event source %s (type %s) returned error, disabling: %m", - strna(s->description), event_source_type_to_string(s->type)); - - 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_errno(r, "Prepare callback of event source %s (type %s) returned error, disabling: %m", - strna(s->description), event_source_type_to_string(s->type)); - - 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_INITIAL; - 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); - - /* Make sure we never set the watchdog to 0, which tells the - * kernel to disable it. */ - if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0) - its.it_value.tv_nsec = 1; - - 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_prepare(sd_event *e) { - int r; - - 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_INITIAL, -EBUSY); - - if (e->exit_requested) - goto pending; - - e->iteration++; - - e->state = SD_EVENT_PREPARING; - r = event_prepare(e); - e->state = SD_EVENT_INITIAL; - if (r < 0) - return r; - - r = event_arm_timer(e, &e->realtime); - if (r < 0) - return r; - - r = event_arm_timer(e, &e->boottime); - if (r < 0) - return r; - - r = event_arm_timer(e, &e->monotonic); - if (r < 0) - return r; - - r = event_arm_timer(e, &e->realtime_alarm); - if (r < 0) - return r; - - r = event_arm_timer(e, &e->boottime_alarm); - if (r < 0) - return r; - - if (event_next_pending(e) || e->need_process_child) - goto pending; - - e->state = SD_EVENT_ARMED; - - return 0; - -pending: - e->state = SD_EVENT_ARMED; - r = sd_event_wait(e, 0); - if (r == 0) - e->state = SD_EVENT_ARMED; - - return r; -} - -_public_ int sd_event_wait(sd_event *e, uint64_t timeout) { - struct epoll_event *ev_queue; - unsigned ev_queue_max; - int r, m, i; - - 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_ARMED, -EBUSY); - - if (e->exit_requested) { - e->state = SD_EVENT_PENDING; - return 1; - } - - ev_queue_max = MAX(e->n_sources, 1u); - 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) { - if (errno == EINTR) { - e->state = SD_EVENT_PENDING; - return 1; - } - - r = -errno; - goto finish; - } - - dual_timestamp_get(&e->timestamp); - if (clock_boottime_supported()) - e->timestamp_boottime = now(CLOCK_BOOTTIME); - - for (i = 0; i < m; i++) { - - if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG)) - r = flush_timer(e, e->watchdog_fd, ev_queue[i].events, NULL); - else { - WakeupType *t = ev_queue[i].data.ptr; - - switch (*t) { - - case WAKEUP_EVENT_SOURCE: - r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events); - break; - - case WAKEUP_CLOCK_DATA: { - struct clock_data *d = ev_queue[i].data.ptr; - r = flush_timer(e, d->fd, ev_queue[i].events, &d->next); - break; - } - - case WAKEUP_SIGNAL_DATA: - r = process_signal(e, ev_queue[i].data.ptr, ev_queue[i].events); - break; - - default: - assert_not_reached("Invalid wake-up pointer"); - } - } - if (r < 0) - goto finish; - } - - r = process_watchdog(e); - if (r < 0) - goto finish; - - r = process_timer(e, e->timestamp.realtime, &e->realtime); - if (r < 0) - goto finish; - - r = process_timer(e, e->timestamp_boottime, &e->boottime); - if (r < 0) - goto finish; - - r = process_timer(e, e->timestamp.monotonic, &e->monotonic); - if (r < 0) - goto finish; - - r = process_timer(e, e->timestamp.realtime, &e->realtime_alarm); - if (r < 0) - goto finish; - - r = process_timer(e, e->timestamp_boottime, &e->boottime_alarm); - if (r < 0) - goto finish; - - if (e->need_process_child) { - r = process_child(e); - if (r < 0) - goto finish; - } - - if (event_next_pending(e)) { - e->state = SD_EVENT_PENDING; - - return 1; - } - - r = 0; - -finish: - e->state = SD_EVENT_INITIAL; - - return r; -} - -_public_ int sd_event_dispatch(sd_event *e) { - sd_event_source *p; - int r; - - 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_PENDING, -EBUSY); - - if (e->exit_requested) - return dispatch_exit(e); - - p = event_next_pending(e); - if (p) { - sd_event_ref(e); - - e->state = SD_EVENT_RUNNING; - r = source_dispatch(p); - e->state = SD_EVENT_INITIAL; - - sd_event_unref(e); - - return r; - } - - e->state = SD_EVENT_INITIAL; - - return 1; -} - -static void event_log_delays(sd_event *e) { - char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1]; - unsigned i; - int o; - - for (i = o = 0; i < ELEMENTSOF(e->delays); i++) { - o += snprintf(&b[o], sizeof(b) - o, "%u ", e->delays[i]); - e->delays[i] = 0; - } - log_debug("Event loop iterations: %.*s", o, b); -} - -_public_ int sd_event_run(sd_event *e, uint64_t timeout) { - int r; - - 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_INITIAL, -EBUSY); - - if (e->profile_delays && e->last_run) { - usec_t this_run; - unsigned l; - - this_run = now(CLOCK_MONOTONIC); - - l = u64log2(this_run - e->last_run); - assert(l < sizeof(e->delays)); - e->delays[l]++; - - if (this_run - e->last_log >= 5*USEC_PER_SEC) { - event_log_delays(e); - e->last_log = this_run; - } - } - - r = sd_event_prepare(e); - if (r == 0) - /* There was nothing? Then wait... */ - r = sd_event_wait(e, timeout); - - if (e->profile_delays) - e->last_run = now(CLOCK_MONOTONIC); - - if (r > 0) { - /* There's something now, then let's dispatch it */ - r = sd_event_dispatch(e); - if (r < 0) - return r; - - return 1; - } - - 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_INITIAL, -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_fd(sd_event *e) { - - assert_return(e, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - - return e->epoll_fd; -} - -_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_now(sd_event *e, clockid_t clock, uint64_t *usec) { - assert_return(e, -EINVAL); - assert_return(usec, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - assert_return(IN_SET(clock, - CLOCK_REALTIME, - CLOCK_REALTIME_ALARM, - CLOCK_MONOTONIC, - CLOCK_BOOTTIME, - CLOCK_BOOTTIME_ALARM), -EOPNOTSUPP); - - if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported()) - return -EOPNOTSUPP; - - if (!dual_timestamp_is_set(&e->timestamp)) { - /* Implicitly fall back to now() if we never ran - * before and thus have no cached time. */ - *usec = now(clock); - return 1; - } - - switch (clock) { - - case CLOCK_REALTIME: - case CLOCK_REALTIME_ALARM: - *usec = e->timestamp.realtime; - break; - - case CLOCK_MONOTONIC: - *usec = e->timestamp.monotonic; - break; - - case CLOCK_BOOTTIME: - case CLOCK_BOOTTIME_ALARM: - *usec = e->timestamp_boottime; - break; - - default: - assert_not_reached("Unknown clock?"); - } - - return 0; -} - -_public_ int sd_event_default(sd_event **ret) { - - static thread_local sd_event *default_event = NULL; - sd_event *e = NULL; - 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); - e->watchdog_fd = safe_close(e->watchdog_fd); - } - } - - e->watchdog = !!b; - return e->watchdog; - -fail: - e->watchdog_fd = safe_close(e->watchdog_fd); - 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 deleted file mode 100644 index 289114490c..0000000000 --- a/src/libsystemd/sd-event/test-event.c +++ /dev/null @@ -1,361 +0,0 @@ -/*** - 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 . -***/ - -#include "sd-event.h" - -#include "fd-util.h" -#include "log.h" -#include "macro.h" -#include "signal-util.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_se(s); - assert_se(si); - - log_info("got child on %c", PTR_TO_INT(userdata)); - - assert_se(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 = NULL; - pid_t pid; - - assert_se(s); - assert_se(si); - - log_info("got signal on %c", PTR_TO_INT(userdata)); - - assert_se(userdata == INT_TO_PTR('e')); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); - - pid = fork(); - assert_se(pid >= 0); - - if (pid == 0) - _exit(0); - - assert_se(sd_event_add_child(sd_event_source_get_event(s), &p, pid, WEXITED, child_handler, INT_TO_PTR('f')) >= 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 = NULL; - - assert_se(s); - - log_info("got defer on %c", PTR_TO_INT(userdata)); - - assert_se(userdata == INT_TO_PTR('d')); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGUSR1, -1) >= 0); - - assert_se(sd_event_add_signal(sd_event_source_get_event(s), &p, SIGUSR1, signal_handler, INT_TO_PTR('e')) >= 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), &p, defer_handler, INT_TO_PTR('d')) >= 0); - assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0); - } else { - assert_se(!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; -} - -static bool got_post = false; - -static int post_handler(sd_event_source *s, void *userdata) { - log_info("got post handler"); - - got_post = true; - - return 2; -} - -static void test_basic(void) { - 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 }; - uint64_t event_now; - - 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_now(e, CLOCK_MONOTONIC, &event_now) > 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, &t, k[0], EPOLLIN, unref_handler, NULL) >= 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, &w, d[0], EPOLLIN, io_handler, INT_TO_PTR('d')) >= 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, &x, a[0], EPOLLIN, io_handler, INT_TO_PTR('a')) >= 0); - assert_se(sd_event_add_io(e, &y, b[0], EPOLLIN, io_handler, INT_TO_PTR('b')) >= 0); - assert_se(sd_event_add_time(e, &z, CLOCK_MONOTONIC, 0, 0, time_handler, INT_TO_PTR('c')) >= 0); - assert_se(sd_event_add_exit(e, &q, exit_handler, INT_TO_PTR('g')) >= 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); - - /* Test for floating event sources */ - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+1, -1) >= 0); - assert_se(sd_event_add_signal(e, NULL, SIGRTMIN+1, NULL, NULL) >= 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_add_post(e, NULL, post_handler, NULL) >= 0); - assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0); - assert_se(sd_event_source_set_time(z, event_now + 200 * USEC_PER_MSEC) >= 0); - assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0); - - assert_se(sd_event_loop(e) >= 0); - assert_se(got_post); - assert_se(got_exit); - - sd_event_source_unref(z); - sd_event_source_unref(q); - - sd_event_source_unref(w); - - sd_event_unref(e); - - safe_close_pair(a); - safe_close_pair(b); - safe_close_pair(d); - safe_close_pair(k); -} - -static void test_sd_event_now(void) { - _cleanup_(sd_event_unrefp) sd_event *e = NULL; - uint64_t event_now; - - assert_se(sd_event_new(&e) >= 0); - assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0); - assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) > 0); - assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) > 0); - if (clock_boottime_supported()) { - assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0); - assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0); - } - assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP); - assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP); - - assert_se(sd_event_run(e, 0) == 0); - - assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0); - assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) == 0); - assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) == 0); - if (clock_boottime_supported()) { - assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0); - assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0); - } - assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP); - assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP); -} - -static int last_rtqueue_sigval = 0; -static int n_rtqueue = 0; - -static int rtqueue_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - last_rtqueue_sigval = si->ssi_int; - n_rtqueue++; - return 0; -} - -static void test_rtqueue(void) { - sd_event_source *u = NULL, *v = NULL, *s = NULL; - sd_event *e = NULL; - - assert_se(sd_event_default(&e) >= 0); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+2, SIGRTMIN+3, SIGUSR2, -1) >= 0); - assert_se(sd_event_add_signal(e, &u, SIGRTMIN+2, rtqueue_handler, NULL) >= 0); - assert_se(sd_event_add_signal(e, &v, SIGRTMIN+3, rtqueue_handler, NULL) >= 0); - assert_se(sd_event_add_signal(e, &s, SIGUSR2, rtqueue_handler, NULL) >= 0); - - assert_se(sd_event_source_set_priority(v, -10) >= 0); - - assert(sigqueue(getpid(), SIGRTMIN+2, (union sigval) { .sival_int = 1 }) >= 0); - assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 2 }) >= 0); - assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 3 }) >= 0); - assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 4 }) >= 0); - assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 5 }) >= 0); - - assert_se(n_rtqueue == 0); - assert_se(last_rtqueue_sigval == 0); - - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - assert_se(n_rtqueue == 1); - assert_se(last_rtqueue_sigval == 2); /* first SIGRTMIN+3 */ - - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - assert_se(n_rtqueue == 2); - assert_se(last_rtqueue_sigval == 4); /* second SIGRTMIN+3 */ - - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - assert_se(n_rtqueue == 3); - assert_se(last_rtqueue_sigval == 3); /* first SIGUSR2 */ - - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - assert_se(n_rtqueue == 4); - assert_se(last_rtqueue_sigval == 1); /* SIGRTMIN+2 */ - - assert_se(sd_event_run(e, 0) == 0); /* the other SIGUSR2 is dropped, because the first one was still queued */ - assert_se(n_rtqueue == 4); - assert_se(last_rtqueue_sigval == 1); - - sd_event_source_unref(u); - sd_event_source_unref(v); - sd_event_source_unref(s); - - sd_event_unref(e); -} - -int main(int argc, char *argv[]) { - - log_set_max_level(LOG_DEBUG); - log_parse_environment(); - - test_basic(); - test_sd_event_now(); - test_rtqueue(); - - return 0; -} diff --git a/src/libsystemd/sd-hwdb/Makefile b/src/libsystemd/sd-hwdb/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-hwdb/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-hwdb/hwdb-internal.h b/src/libsystemd/sd-hwdb/hwdb-internal.h deleted file mode 100644 index 8ffb5e5c74..0000000000 --- a/src/libsystemd/sd-hwdb/hwdb-internal.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers - - 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 . -***/ - -#include "sparse-endian.h" -#include "util.h" - -#define HWDB_SIG { 'K', 'S', 'L', 'P', 'H', 'H', 'R', 'H' } - -/* on-disk trie objects */ -struct trie_header_f { - uint8_t signature[8]; - - /* version of tool which created the file */ - le64_t tool_version; - le64_t file_size; - - /* size of structures to allow them to grow */ - le64_t header_size; - le64_t node_size; - le64_t child_entry_size; - le64_t value_entry_size; - - /* offset of the root trie node */ - le64_t nodes_root_off; - - /* size of the nodes and string section */ - le64_t nodes_len; - le64_t strings_len; -} _packed_; - -struct trie_node_f { - /* prefix of lookup string, shared by all children */ - le64_t prefix_off; - /* size of children entry array appended to the node */ - uint8_t children_count; - uint8_t padding[7]; - /* size of value entry array appended to the node */ - le64_t values_count; -} _packed_; - -/* array of child entries, follows directly the node record */ -struct trie_child_entry_f { - /* index of the child node */ - uint8_t c; - uint8_t padding[7]; - /* offset of the child node */ - le64_t child_off; -} _packed_; - -/* array of value entries, follows directly the node record/child array */ -struct trie_value_entry_f { - le64_t key_off; - le64_t value_off; -} _packed_; diff --git a/src/libsystemd/sd-hwdb/hwdb-util.h b/src/libsystemd/sd-hwdb/hwdb-util.h deleted file mode 100644 index 5e21e5008b..0000000000 --- a/src/libsystemd/sd-hwdb/hwdb-util.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen - - 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 . -***/ - -#include "sd-hwdb.h" - -#include "util.h" - -bool hwdb_validate(sd_hwdb *hwdb); diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c deleted file mode 100644 index 062fa97b17..0000000000 --- a/src/libsystemd/sd-hwdb/sd-hwdb.c +++ /dev/null @@ -1,470 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers - Copyright 2008 Alan Jenkins - Copyright 2014 Tom Gundersen - - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "sd-hwdb.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "hashmap.h" -#include "hwdb-internal.h" -#include "hwdb-util.h" -#include "refcnt.h" -#include "string-util.h" - -struct sd_hwdb { - RefCount n_ref; - int refcount; - - FILE *f; - struct stat st; - union { - struct trie_header_f *head; - const char *map; - }; - - char *modalias; - - OrderedHashmap *properties; - Iterator properties_iterator; - bool properties_modified; -}; - -struct linebuf { - char bytes[LINE_MAX]; - size_t size; - size_t len; -}; - -static void linebuf_init(struct linebuf *buf) { - buf->size = 0; - buf->len = 0; -} - -static const char *linebuf_get(struct linebuf *buf) { - if (buf->len + 1 >= sizeof(buf->bytes)) - return NULL; - buf->bytes[buf->len] = '\0'; - return buf->bytes; -} - -static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) { - if (buf->len + len >= sizeof(buf->bytes)) - return false; - memcpy(buf->bytes + buf->len, s, len); - buf->len += len; - return true; -} - -static bool linebuf_add_char(struct linebuf *buf, char c) { - if (buf->len + 1 >= sizeof(buf->bytes)) - return false; - buf->bytes[buf->len++] = c; - return true; -} - -static void linebuf_rem(struct linebuf *buf, size_t count) { - assert(buf->len >= count); - buf->len -= count; -} - -static void linebuf_rem_char(struct linebuf *buf) { - linebuf_rem(buf, 1); -} - -static const struct trie_child_entry_f *trie_node_children(sd_hwdb *hwdb, const struct trie_node_f *node) { - return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size)); -} - -static const struct trie_value_entry_f *trie_node_values(sd_hwdb *hwdb, const struct trie_node_f *node) { - const char *base = (const char *)node; - - base += le64toh(hwdb->head->node_size); - base += node->children_count * le64toh(hwdb->head->child_entry_size); - return (const struct trie_value_entry_f *)base; -} - -static const struct trie_node_f *trie_node_from_off(sd_hwdb *hwdb, le64_t off) { - return (const struct trie_node_f *)(hwdb->map + le64toh(off)); -} - -static const char *trie_string(sd_hwdb *hwdb, le64_t off) { - return hwdb->map + le64toh(off); -} - -static int trie_children_cmp_f(const void *v1, const void *v2) { - const struct trie_child_entry_f *n1 = v1; - const struct trie_child_entry_f *n2 = v2; - - return n1->c - n2->c; -} - -static const struct trie_node_f *node_lookup_f(sd_hwdb *hwdb, const struct trie_node_f *node, uint8_t c) { - struct trie_child_entry_f *child; - struct trie_child_entry_f search; - - search.c = c; - child = bsearch(&search, trie_node_children(hwdb, node), node->children_count, - le64toh(hwdb->head->child_entry_size), trie_children_cmp_f); - if (child) - return trie_node_from_off(hwdb, child->child_off); - return NULL; -} - -static int hwdb_add_property(sd_hwdb *hwdb, const char *key, const char *value) { - int r; - - assert(hwdb); - assert(key); - assert(value); - - /* - * Silently ignore all properties which do not start with a - * space; future extensions might use additional prefixes. - */ - if (key[0] != ' ') - return 0; - - key++; - - r = ordered_hashmap_ensure_allocated(&hwdb->properties, &string_hash_ops); - if (r < 0) - return r; - - r = ordered_hashmap_replace(hwdb->properties, key, (char*)value); - if (r < 0) - return r; - - hwdb->properties_modified = true; - - return 0; -} - -static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t p, - struct linebuf *buf, const char *search) { - size_t len; - size_t i; - const char *prefix; - int err; - - prefix = trie_string(hwdb, node->prefix_off); - len = strlen(prefix + p); - linebuf_add(buf, prefix + p, len); - - for (i = 0; i < node->children_count; i++) { - const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i]; - - linebuf_add_char(buf, child->c); - err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search); - if (err < 0) - return err; - linebuf_rem_char(buf); - } - - if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0) - for (i = 0; i < le64toh(node->values_count); i++) { - err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off), - trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off)); - if (err < 0) - return err; - } - - linebuf_rem(buf, len); - return 0; -} - -static int trie_search_f(sd_hwdb *hwdb, const char *search) { - struct linebuf buf; - const struct trie_node_f *node; - size_t i = 0; - int err; - - linebuf_init(&buf); - - node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off); - while (node) { - const struct trie_node_f *child; - size_t p = 0; - - if (node->prefix_off) { - uint8_t c; - - for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) { - if (c == '*' || c == '?' || c == '[') - return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p); - if (c != search[i + p]) - return 0; - } - i += p; - } - - child = node_lookup_f(hwdb, node, '*'); - if (child) { - linebuf_add_char(&buf, '*'); - err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i); - if (err < 0) - return err; - linebuf_rem_char(&buf); - } - - child = node_lookup_f(hwdb, node, '?'); - if (child) { - linebuf_add_char(&buf, '?'); - err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i); - if (err < 0) - return err; - linebuf_rem_char(&buf); - } - - child = node_lookup_f(hwdb, node, '['); - if (child) { - linebuf_add_char(&buf, '['); - err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i); - if (err < 0) - return err; - linebuf_rem_char(&buf); - } - - if (search[i] == '\0') { - size_t n; - - for (n = 0; n < le64toh(node->values_count); n++) { - err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off), - trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off)); - if (err < 0) - return err; - } - return 0; - } - - child = node_lookup_f(hwdb, node, search[i]); - node = child; - i++; - } - return 0; -} - -static const char hwdb_bin_paths[] = - "/etc/systemd/hwdb/hwdb.bin\0" - "/etc/udev/hwdb.bin\0" - "/usr/lib/systemd/hwdb/hwdb.bin\0" -#ifdef HAVE_SPLIT_USR - "/lib/systemd/hwdb/hwdb.bin\0" -#endif - UDEVLIBEXECDIR "/hwdb.bin\0"; - -_public_ int sd_hwdb_new(sd_hwdb **ret) { - _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; - const char *hwdb_bin_path; - const char sig[] = HWDB_SIG; - - assert_return(ret, -EINVAL); - - hwdb = new0(sd_hwdb, 1); - if (!hwdb) - return -ENOMEM; - - hwdb->n_ref = REFCNT_INIT; - - /* find hwdb.bin in hwdb_bin_paths */ - NULSTR_FOREACH(hwdb_bin_path, hwdb_bin_paths) { - hwdb->f = fopen(hwdb_bin_path, "re"); - if (hwdb->f) - break; - else if (errno == ENOENT) - continue; - else - return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path); - } - - if (!hwdb->f) { - log_debug("hwdb.bin does not exist, please run udevadm hwdb --update"); - return -ENOENT; - } - - if (fstat(fileno(hwdb->f), &hwdb->st) < 0 || - (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8) - return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path); - - hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0); - if (hwdb->map == MAP_FAILED) - return log_debug_errno(errno, "error mapping %s: %m", hwdb_bin_path); - - if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 || - (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) { - log_debug("error recognizing the format of %s", hwdb_bin_path); - return -EINVAL; - } - - log_debug("=== trie on-disk ==="); - log_debug("tool version: %"PRIu64, le64toh(hwdb->head->tool_version)); - log_debug("file size: %8"PRIi64" bytes", hwdb->st.st_size); - log_debug("header size %8"PRIu64" bytes", le64toh(hwdb->head->header_size)); - log_debug("strings %8"PRIu64" bytes", le64toh(hwdb->head->strings_len)); - log_debug("nodes %8"PRIu64" bytes", le64toh(hwdb->head->nodes_len)); - - *ret = hwdb; - hwdb = NULL; - - return 0; -} - -_public_ sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb) { - assert_return(hwdb, NULL); - - assert_se(REFCNT_INC(hwdb->n_ref) >= 2); - - return hwdb; -} - -_public_ sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb) { - if (hwdb && REFCNT_DEC(hwdb->n_ref) == 0) { - if (hwdb->map) - munmap((void *)hwdb->map, hwdb->st.st_size); - safe_fclose(hwdb->f); - free(hwdb->modalias); - ordered_hashmap_free(hwdb->properties); - free(hwdb); - } - - return NULL; -} - -bool hwdb_validate(sd_hwdb *hwdb) { - bool found = false; - const char* p; - struct stat st; - - if (!hwdb) - return false; - if (!hwdb->f) - return false; - - /* if hwdb.bin doesn't exist anywhere, we need to update */ - NULSTR_FOREACH(p, hwdb_bin_paths) { - if (stat(p, &st) >= 0) { - found = true; - break; - } - } - if (!found) - return true; - - if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim)) - return true; - return false; -} - -static int properties_prepare(sd_hwdb *hwdb, const char *modalias) { - _cleanup_free_ char *mod = NULL; - int r; - - assert(hwdb); - assert(modalias); - - if (streq_ptr(modalias, hwdb->modalias)) - return 0; - - mod = strdup(modalias); - if (!mod) - return -ENOMEM; - - ordered_hashmap_clear(hwdb->properties); - - hwdb->properties_modified = true; - - r = trie_search_f(hwdb, modalias); - if (r < 0) - return r; - - free(hwdb->modalias); - hwdb->modalias = mod; - mod = NULL; - - return 0; -} - -_public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) { - const char *value; - int r; - - assert_return(hwdb, -EINVAL); - assert_return(hwdb->f, -EINVAL); - assert_return(modalias, -EINVAL); - assert_return(_value, -EINVAL); - - r = properties_prepare(hwdb, modalias); - if (r < 0) - return r; - - value = ordered_hashmap_get(hwdb->properties, key); - if (!value) - return -ENOENT; - - *_value = value; - - return 0; -} - -_public_ int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias) { - int r; - - assert_return(hwdb, -EINVAL); - assert_return(hwdb->f, -EINVAL); - assert_return(modalias, -EINVAL); - - r = properties_prepare(hwdb, modalias); - if (r < 0) - return r; - - hwdb->properties_modified = false; - hwdb->properties_iterator = ITERATOR_FIRST; - - return 0; -} - -_public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value) { - const void *k; - void *v; - - assert_return(hwdb, -EINVAL); - assert_return(key, -EINVAL); - assert_return(value, -EINVAL); - - if (hwdb->properties_modified) - return -EAGAIN; - - ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, &v, &k); - if (!k) - return 0; - - *key = k; - *value = v; - - return 1; -} diff --git a/src/libsystemd/sd-id128/Makefile b/src/libsystemd/sd-id128/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-id128/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c deleted file mode 100644 index d9c0116f60..0000000000 --- a/src/libsystemd/sd-id128/sd-id128.c +++ /dev/null @@ -1,225 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 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 . -***/ - -#include -#include -#include - -#include "sd-id128.h" - -#include "fd-util.h" -#include "hexdecoct.h" -#include "io-util.h" -#include "macro.h" -#include "random-util.h" -#include "util.h" - -_public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) { - unsigned n; - - assert_return(s, NULL); - - for (n = 0; n < 16; n++) { - s[n*2] = hexchar(id.bytes[n] >> 4); - s[n*2+1] = hexchar(id.bytes[n] & 0xF); - } - - s[32] = 0; - - return s; -} - -_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) { - unsigned n, i; - sd_id128_t t; - bool is_guid = false; - - assert_return(s, -EINVAL); - assert_return(ret, -EINVAL); - - for (n = 0, i = 0; n < 16;) { - int a, b; - - if (s[i] == '-') { - /* Is this a GUID? Then be nice, and skip over - * the dashes */ - - if (i == 8) - is_guid = true; - else if (i == 13 || i == 18 || i == 23) { - if (!is_guid) - return -EINVAL; - } else - return -EINVAL; - - i++; - continue; - } - - a = unhexchar(s[i++]); - if (a < 0) - return -EINVAL; - - b = unhexchar(s[i++]); - if (b < 0) - return -EINVAL; - - t.bytes[n++] = (a << 4) | b; - } - - if (i != (is_guid ? 36 : 32)) - return -EINVAL; - - if (s[i] != 0) - return -EINVAL; - - *ret = t; - return 0; -} - -static sd_id128_t make_v4_uuid(sd_id128_t id) { - /* Stolen from generate_random_uuid() of drivers/char/random.c - * in the kernel sources */ - - /* Set UUID version to 4 --- truly random generation */ - id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40; - - /* Set the UUID variant to DCE */ - id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80; - - return id; -} - -_public_ int sd_id128_get_machine(sd_id128_t *ret) { - static thread_local sd_id128_t saved_machine_id; - static thread_local bool saved_machine_id_valid = false; - _cleanup_close_ int fd = -1; - char buf[33]; - unsigned j; - sd_id128_t t; - int r; - - assert_return(ret, -EINVAL); - - if (saved_machine_id_valid) { - *ret = saved_machine_id; - return 0; - } - - fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return -errno; - - r = loop_read_exact(fd, buf, 33, false); - if (r < 0) - return r; - if (buf[32] !='\n') - return -EIO; - - for (j = 0; j < 16; j++) { - int a, b; - - a = unhexchar(buf[j*2]); - b = unhexchar(buf[j*2+1]); - - if (a < 0 || b < 0) - return -EIO; - - t.bytes[j] = a << 4 | b; - } - - saved_machine_id = t; - saved_machine_id_valid = true; - - *ret = t; - return 0; -} - -_public_ int sd_id128_get_boot(sd_id128_t *ret) { - static thread_local sd_id128_t saved_boot_id; - static thread_local bool saved_boot_id_valid = false; - _cleanup_close_ int fd = -1; - char buf[36]; - unsigned j; - sd_id128_t t; - char *p; - int r; - - assert_return(ret, -EINVAL); - - if (saved_boot_id_valid) { - *ret = saved_boot_id; - return 0; - } - - fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return -errno; - - r = loop_read_exact(fd, buf, 36, false); - if (r < 0) - return r; - - for (j = 0, p = buf; j < 16; j++) { - int a, b; - - if (p >= buf + 35) - return -EIO; - - if (*p == '-') { - p++; - if (p >= buf + 35) - return -EIO; - } - - a = unhexchar(p[0]); - b = unhexchar(p[1]); - - if (a < 0 || b < 0) - return -EIO; - - t.bytes[j] = a << 4 | b; - - p += 2; - } - - saved_boot_id = t; - saved_boot_id_valid = true; - - *ret = t; - return 0; -} - -_public_ int sd_id128_randomize(sd_id128_t *ret) { - sd_id128_t t; - int r; - - assert_return(ret, -EINVAL); - - r = dev_urandom(&t, sizeof(t)); - if (r < 0) - return r; - - /* Turn this into a valid v4 UUID, to be nice. Note that we - * only guarantee this for newly generated UUIDs, not for - * pre-existing ones. */ - - *ret = make_v4_uuid(t); - return 0; -} diff --git a/src/libsystemd/sd-login/Makefile b/src/libsystemd/sd-login/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd/sd-login/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c deleted file mode 100644 index 9d4f187502..0000000000 --- a/src/libsystemd/sd-login/sd-login.c +++ /dev/null @@ -1,1062 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 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 . -***/ - -#include -#include -#include -#include -#include - -#include "sd-login.h" - -#include "alloc-util.h" -#include "cgroup-util.h" -#include "dirent-util.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "fs-util.h" -#include "hostname-util.h" -#include "io-util.h" -#include "login-util.h" -#include "macro.h" -#include "parse-util.h" -#include "path-util.h" -#include "socket-util.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" -#include "util.h" - -/* Error codes: - * - * invalid input parameters → -EINVAL - * invalid fd → -EBADF - * process does not exist → -ESRCH - * cgroup does not exist → -ENOENT - * machine, session does not exist → -ENXIO - * requested metadata on object is missing → -ENODATA - */ - -_public_ int sd_pid_get_session(pid_t pid, char **session) { - - assert_return(pid >= 0, -EINVAL); - assert_return(session, -EINVAL); - - return cg_pid_get_session(pid, session); -} - -_public_ int sd_pid_get_unit(pid_t pid, char **unit) { - - assert_return(pid >= 0, -EINVAL); - assert_return(unit, -EINVAL); - - return cg_pid_get_unit(pid, unit); -} - -_public_ int sd_pid_get_user_unit(pid_t pid, char **unit) { - - assert_return(pid >= 0, -EINVAL); - assert_return(unit, -EINVAL); - - return cg_pid_get_user_unit(pid, unit); -} - -_public_ int sd_pid_get_machine_name(pid_t pid, char **name) { - - assert_return(pid >= 0, -EINVAL); - assert_return(name, -EINVAL); - - return cg_pid_get_machine_name(pid, name); -} - -_public_ int sd_pid_get_slice(pid_t pid, char **slice) { - - assert_return(pid >= 0, -EINVAL); - assert_return(slice, -EINVAL); - - return cg_pid_get_slice(pid, slice); -} - -_public_ int sd_pid_get_user_slice(pid_t pid, char **slice) { - - assert_return(pid >= 0, -EINVAL); - assert_return(slice, -EINVAL); - - return cg_pid_get_user_slice(pid, slice); -} - -_public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) { - - assert_return(pid >= 0, -EINVAL); - assert_return(uid, -EINVAL); - - return cg_pid_get_owner_uid(pid, uid); -} - -_public_ int sd_pid_get_cgroup(pid_t pid, char **cgroup) { - char *c; - int r; - - assert_return(pid >= 0, -EINVAL); - assert_return(cgroup, -EINVAL); - - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &c); - if (r < 0) - return r; - - /* The internal APIs return the empty string for the root - * cgroup, let's return the "/" in the public APIs instead, as - * that's easier and less ambigious for people to grok. */ - if (isempty(c)) { - free(c); - c = strdup("/"); - if (!c) - return -ENOMEM; - - } - - *cgroup = c; - return 0; -} - -_public_ int sd_peer_get_session(int fd, char **session) { - struct ucred ucred = {}; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(session, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_session(ucred.pid, session); -} - -_public_ int sd_peer_get_owner_uid(int fd, uid_t *uid) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(uid, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_owner_uid(ucred.pid, uid); -} - -_public_ int sd_peer_get_unit(int fd, char **unit) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(unit, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_unit(ucred.pid, unit); -} - -_public_ int sd_peer_get_user_unit(int fd, char **unit) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(unit, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_user_unit(ucred.pid, unit); -} - -_public_ int sd_peer_get_machine_name(int fd, char **machine) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(machine, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_machine_name(ucred.pid, machine); -} - -_public_ int sd_peer_get_slice(int fd, char **slice) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(slice, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_slice(ucred.pid, slice); -} - -_public_ int sd_peer_get_user_slice(int fd, char **slice) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(slice, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_user_slice(ucred.pid, slice); -} - -_public_ int sd_peer_get_cgroup(int fd, char **cgroup) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(cgroup, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return sd_pid_get_cgroup(ucred.pid, cgroup); -} - -static int file_of_uid(uid_t uid, char **p) { - - assert_return(uid_is_valid(uid), -EINVAL); - assert(p); - - if (asprintf(p, "/run/systemd/users/" UID_FMT, uid) < 0) - return -ENOMEM; - - return 0; -} - -_public_ int sd_uid_get_state(uid_t uid, char**state) { - _cleanup_free_ char *p = NULL; - char *s = NULL; - int r; - - assert_return(state, -EINVAL); - - r = file_of_uid(uid, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, "STATE", &s, NULL); - if (r == -ENOENT) { - free(s); - s = strdup("offline"); - if (!s) - return -ENOMEM; - - } - if (r < 0) { - free(s); - return r; - } - if (isempty(s)) { - free(s); - return -EIO; - } - - *state = s; - return 0; -} - -_public_ int sd_uid_get_display(uid_t uid, char **session) { - _cleanup_free_ char *p = NULL, *s = NULL; - int r; - - assert_return(session, -EINVAL); - - r = file_of_uid(uid, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, "DISPLAY", &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - *session = s; - s = NULL; - - return 0; -} - -static int file_of_seat(const char *seat, char **_p) { - char *p; - int r; - - assert(_p); - - if (seat) { - if (!filename_is_valid(seat)) - return -EINVAL; - - p = strappend("/run/systemd/seats/", seat); - } else { - _cleanup_free_ char *buf = NULL; - - r = sd_session_get_seat(NULL, &buf); - if (r < 0) - return r; - - p = strappend("/run/systemd/seats/", buf); - } - - if (!p) - return -ENOMEM; - - *_p = p; - p = NULL; - return 0; -} - -_public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) { - _cleanup_free_ char *t = NULL, *s = NULL, *p = NULL; - size_t l; - int r; - const char *word, *variable, *state; - - assert_return(uid_is_valid(uid), -EINVAL); - - r = file_of_seat(seat, &p); - if (r < 0) - return r; - - variable = require_active ? "ACTIVE_UID" : "UIDS"; - - r = parse_env_file(p, NEWLINE, variable, &s, NULL); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - if (isempty(s)) - return 0; - - if (asprintf(&t, UID_FMT, uid) < 0) - return -ENOMEM; - - FOREACH_WORD(word, l, s, state) - if (strneq(t, word, l)) - return 1; - - return 0; -} - -static int uid_get_array(uid_t uid, const char *variable, char ***array) { - _cleanup_free_ char *p = NULL, *s = NULL; - char **a; - int r; - - assert(variable); - - r = file_of_uid(uid, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, variable, &s, NULL); - if (r == -ENOENT || (r >= 0 && isempty(s))) { - if (array) - *array = NULL; - return 0; - } - if (r < 0) - return r; - - a = strv_split(s, " "); - if (!a) - return -ENOMEM; - - strv_uniq(a); - r = strv_length(a); - - if (array) - *array = a; - else - strv_free(a); - - return r; -} - -_public_ int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions) { - return uid_get_array( - uid, - require_active == 0 ? "ONLINE_SESSIONS" : - require_active > 0 ? "ACTIVE_SESSIONS" : - "SESSIONS", - sessions); -} - -_public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) { - return uid_get_array( - uid, - require_active == 0 ? "ONLINE_SEATS" : - require_active > 0 ? "ACTIVE_SEATS" : - "SEATS", - seats); -} - -static int file_of_session(const char *session, char **_p) { - char *p; - int r; - - assert(_p); - - if (session) { - if (!session_id_valid(session)) - return -EINVAL; - - p = strappend("/run/systemd/sessions/", session); - } else { - _cleanup_free_ char *buf = NULL; - - r = sd_pid_get_session(0, &buf); - if (r < 0) - return r; - - p = strappend("/run/systemd/sessions/", buf); - } - - if (!p) - return -ENOMEM; - - *_p = p; - return 0; -} - -_public_ int sd_session_is_active(const char *session) { - _cleanup_free_ char *p = NULL, *s = NULL; - int r; - - r = file_of_session(session, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (isempty(s)) - return -EIO; - - return parse_boolean(s); -} - -_public_ int sd_session_is_remote(const char *session) { - _cleanup_free_ char *p = NULL, *s = NULL; - int r; - - r = file_of_session(session, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, "REMOTE", &s, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - return parse_boolean(s); -} - -_public_ int sd_session_get_state(const char *session, char **state) { - _cleanup_free_ char *p = NULL, *s = NULL; - int r; - - assert_return(state, -EINVAL); - - r = file_of_session(session, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, "STATE", &s, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (isempty(s)) - return -EIO; - - *state = s; - s = NULL; - - return 0; -} - -_public_ int sd_session_get_uid(const char *session, uid_t *uid) { - int r; - _cleanup_free_ char *p = NULL, *s = NULL; - - assert_return(uid, -EINVAL); - - r = file_of_session(session, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, "UID", &s, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (isempty(s)) - return -EIO; - - return parse_uid(s, uid); -} - -static int session_get_string(const char *session, const char *field, char **value) { - _cleanup_free_ char *p = NULL, *s = NULL; - int r; - - assert_return(value, -EINVAL); - assert(field); - - r = file_of_session(session, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, field, &s, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - *value = s; - s = NULL; - return 0; -} - -_public_ int sd_session_get_seat(const char *session, char **seat) { - return session_get_string(session, "SEAT", seat); -} - -_public_ int sd_session_get_tty(const char *session, char **tty) { - return session_get_string(session, "TTY", tty); -} - -_public_ int sd_session_get_vt(const char *session, unsigned *vtnr) { - _cleanup_free_ char *vtnr_string = NULL; - unsigned u; - int r; - - assert_return(vtnr, -EINVAL); - - r = session_get_string(session, "VTNR", &vtnr_string); - if (r < 0) - return r; - - r = safe_atou(vtnr_string, &u); - if (r < 0) - return r; - - *vtnr = u; - return 0; -} - -_public_ int sd_session_get_service(const char *session, char **service) { - return session_get_string(session, "SERVICE", service); -} - -_public_ int sd_session_get_type(const char *session, char **type) { - return session_get_string(session, "TYPE", type); -} - -_public_ int sd_session_get_class(const char *session, char **class) { - return session_get_string(session, "CLASS", class); -} - -_public_ int sd_session_get_desktop(const char *session, char **desktop) { - _cleanup_free_ char *escaped = NULL; - char *t; - int r; - - assert_return(desktop, -EINVAL); - - r = session_get_string(session, "DESKTOP", &escaped); - if (r < 0) - return r; - - r = cunescape(escaped, 0, &t); - if (r < 0) - return r; - - *desktop = t; - return 0; -} - -_public_ int sd_session_get_display(const char *session, char **display) { - return session_get_string(session, "DISPLAY", display); -} - -_public_ int sd_session_get_remote_user(const char *session, char **remote_user) { - return session_get_string(session, "REMOTE_USER", remote_user); -} - -_public_ int sd_session_get_remote_host(const char *session, char **remote_host) { - return session_get_string(session, "REMOTE_HOST", remote_host); -} - -_public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) { - _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL; - int r; - - assert_return(session || uid, -EINVAL); - - r = file_of_seat(seat, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, - "ACTIVE", &s, - "ACTIVE_UID", &t, - NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - - if (session && !s) - return -ENODATA; - - if (uid && !t) - return -ENODATA; - - if (uid && t) { - r = parse_uid(t, uid); - if (r < 0) - return r; - } - - if (session && s) { - *session = s; - s = NULL; - } - - return 0; -} - -_public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) { - _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL; - _cleanup_strv_free_ char **a = NULL; - _cleanup_free_ uid_t *b = NULL; - unsigned n = 0; - int r; - - r = file_of_seat(seat, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, - "SESSIONS", &s, - "ACTIVE_SESSIONS", &t, - NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - - if (s) { - a = strv_split(s, " "); - if (!a) - return -ENOMEM; - } - - if (uids && t) { - const char *word, *state; - size_t l; - - FOREACH_WORD(word, l, t, state) - n++; - - if (n > 0) { - unsigned i = 0; - - b = new(uid_t, n); - if (!b) - return -ENOMEM; - - FOREACH_WORD(word, l, t, state) { - _cleanup_free_ char *k = NULL; - - k = strndup(word, l); - if (!k) - return -ENOMEM; - - r = parse_uid(k, b + i); - if (r < 0) - continue; - - i++; - } - } - } - - r = strv_length(a); - - if (sessions) { - *sessions = a; - a = NULL; - } - - if (uids) { - *uids = b; - b = NULL; - } - - if (n_uids) - *n_uids = n; - - return r; -} - -static int seat_get_can(const char *seat, const char *variable) { - _cleanup_free_ char *p = NULL, *s = NULL; - int r; - - assert(variable); - - r = file_of_seat(seat, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, - variable, &s, - NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - return parse_boolean(s); -} - -_public_ int sd_seat_can_multi_session(const char *seat) { - return seat_get_can(seat, "CAN_MULTI_SESSION"); -} - -_public_ int sd_seat_can_tty(const char *seat) { - return seat_get_can(seat, "CAN_TTY"); -} - -_public_ int sd_seat_can_graphical(const char *seat) { - return seat_get_can(seat, "CAN_GRAPHICAL"); -} - -_public_ int sd_get_seats(char ***seats) { - return get_files_in_directory("/run/systemd/seats/", seats); -} - -_public_ int sd_get_sessions(char ***sessions) { - return get_files_in_directory("/run/systemd/sessions/", sessions); -} - -_public_ int sd_get_uids(uid_t **users) { - _cleanup_closedir_ DIR *d; - int r = 0; - unsigned n = 0; - _cleanup_free_ uid_t *l = NULL; - - d = opendir("/run/systemd/users/"); - if (!d) - return -errno; - - for (;;) { - struct dirent *de; - int k; - uid_t uid; - - errno = 0; - de = readdir(d); - if (!de && errno > 0) - return -errno; - - if (!de) - break; - - dirent_ensure_type(d, de); - - if (!dirent_is_file(de)) - continue; - - k = parse_uid(de->d_name, &uid); - if (k < 0) - continue; - - if (users) { - if ((unsigned) r >= n) { - uid_t *t; - - n = MAX(16, 2*r); - t = realloc(l, sizeof(uid_t) * n); - if (!t) - return -ENOMEM; - - l = t; - } - - assert((unsigned) r < n); - l[r++] = uid; - } else - r++; - } - - if (users) { - *users = l; - l = NULL; - } - - return r; -} - -_public_ int sd_get_machine_names(char ***machines) { - char **l = NULL, **a, **b; - int r; - - assert_return(machines, -EINVAL); - - r = get_files_in_directory("/run/systemd/machines/", &l); - if (r < 0) - return r; - - if (l) { - r = 0; - - /* Filter out the unit: symlinks */ - for (a = l, b = l; *a; a++) { - if (startswith(*a, "unit:") || !machine_name_is_valid(*a)) - free(*a); - else { - *b = *a; - b++; - r++; - } - } - - *b = NULL; - } - - *machines = l; - return r; -} - -_public_ int sd_machine_get_class(const char *machine, char **class) { - _cleanup_free_ char *c = NULL; - const char *p; - int r; - - assert_return(machine_name_is_valid(machine), -EINVAL); - assert_return(class, -EINVAL); - - p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(p, NEWLINE, "CLASS", &c, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (!c) - return -EIO; - - *class = c; - c = NULL; - - return 0; -} - -_public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) { - _cleanup_free_ char *netif = NULL; - size_t l, allocated = 0, nr = 0; - int *ni = NULL; - const char *p, *word, *state; - int r; - - assert_return(machine_name_is_valid(machine), -EINVAL); - assert_return(ifindices, -EINVAL); - - p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(p, NEWLINE, "NETIF", &netif, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (!netif) { - *ifindices = NULL; - return 0; - } - - FOREACH_WORD(word, l, netif, state) { - char buf[l+1]; - int ifi; - - *(char*) (mempcpy(buf, word, l)) = 0; - - if (parse_ifindex(buf, &ifi) < 0) - continue; - - if (!GREEDY_REALLOC(ni, allocated, nr+1)) { - free(ni); - return -ENOMEM; - } - - ni[nr++] = ifi; - } - - *ifindices = ni; - return nr; -} - -static inline int MONITOR_TO_FD(sd_login_monitor *m) { - return (int) (unsigned long) m - 1; -} - -static inline sd_login_monitor* FD_TO_MONITOR(int fd) { - return (sd_login_monitor*) (unsigned long) (fd + 1); -} - -_public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) { - int fd, k; - bool good = false; - - assert_return(m, -EINVAL); - - fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (fd < 0) - return -errno; - - if (!category || streq(category, "seat")) { - k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE); - if (k < 0) { - safe_close(fd); - return -errno; - } - - good = true; - } - - if (!category || streq(category, "session")) { - k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE); - if (k < 0) { - safe_close(fd); - return -errno; - } - - good = true; - } - - if (!category || streq(category, "uid")) { - k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE); - if (k < 0) { - safe_close(fd); - return -errno; - } - - good = true; - } - - if (!category || streq(category, "machine")) { - k = inotify_add_watch(fd, "/run/systemd/machines/", IN_MOVED_TO|IN_DELETE); - if (k < 0) { - safe_close(fd); - return -errno; - } - - good = true; - } - - if (!good) { - close_nointr(fd); - return -EINVAL; - } - - *m = FD_TO_MONITOR(fd); - return 0; -} - -_public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) { - int fd; - - if (!m) - return NULL; - - fd = MONITOR_TO_FD(m); - close_nointr(fd); - - return NULL; -} - -_public_ int sd_login_monitor_flush(sd_login_monitor *m) { - - assert_return(m, -EINVAL); - - return flush_fd(MONITOR_TO_FD(m)); -} - -_public_ int sd_login_monitor_get_fd(sd_login_monitor *m) { - - assert_return(m, -EINVAL); - - return MONITOR_TO_FD(m); -} - -_public_ int sd_login_monitor_get_events(sd_login_monitor *m) { - - assert_return(m, -EINVAL); - - /* For now we will only return POLLIN here, since we don't - * need anything else ever for inotify. However, let's have - * this API to keep our options open should we later on need - * it. */ - return POLLIN; -} - -_public_ int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec) { - - assert_return(m, -EINVAL); - assert_return(timeout_usec, -EINVAL); - - /* For now we will only return (uint64_t) -1, since we don't - * need any timeout. However, let's have this API to keep our - * options open should we later on need it. */ - *timeout_usec = (uint64_t) -1; - return 0; -} diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c deleted file mode 100644 index c1fd7dd33e..0000000000 --- a/src/libsystemd/sd-login/test-login.c +++ /dev/null @@ -1,264 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 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 . -***/ - -#include -#include - -#include "sd-login.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "formats-util.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -static void test_login(void) { - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_free_ char *pp = NULL, *qq = NULL; - int r, k; - uid_t u, u2; - char *seat, *type, *class, *display, *remote_user, *remote_host, *display_session, *cgroup; - char *session; - char *state; - char *session2; - char *t; - char **seats, **sessions, **machines; - uid_t *uids; - unsigned n; - struct pollfd pollfd; - sd_login_monitor *m = NULL; - - assert_se(sd_pid_get_session(0, &session) == 0); - printf("session = %s\n", session); - - assert_se(sd_pid_get_owner_uid(0, &u2) == 0); - printf("user = "UID_FMT"\n", u2); - - assert_se(sd_pid_get_cgroup(0, &cgroup) == 0); - printf("cgroup = %s\n", cgroup); - free(cgroup); - - display_session = NULL; - r = sd_uid_get_display(u2, &display_session); - assert_se(r >= 0 || r == -ENODATA); - printf("user's display session = %s\n", strna(display_session)); - free(display_session); - - assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0); - sd_peer_get_session(pair[0], &pp); - sd_peer_get_session(pair[1], &qq); - assert_se(streq_ptr(pp, qq)); - - r = sd_uid_get_sessions(u2, false, &sessions); - assert_se(r >= 0); - assert_se(r == (int) strv_length(sessions)); - assert_se(t = strv_join(sessions, ", ")); - strv_free(sessions); - printf("sessions = %s\n", t); - free(t); - - assert_se(r == sd_uid_get_sessions(u2, false, NULL)); - - r = sd_uid_get_seats(u2, false, &seats); - assert_se(r >= 0); - assert_se(r == (int) strv_length(seats)); - assert_se(t = strv_join(seats, ", ")); - strv_free(seats); - printf("seats = %s\n", t); - free(t); - - assert_se(r == sd_uid_get_seats(u2, false, NULL)); - - r = sd_session_is_active(session); - assert_se(r >= 0); - printf("active = %s\n", yes_no(r)); - - r = sd_session_is_remote(session); - assert_se(r >= 0); - printf("remote = %s\n", yes_no(r)); - - r = sd_session_get_state(session, &state); - assert_se(r >= 0); - printf("state = %s\n", state); - free(state); - - assert_se(sd_session_get_uid(session, &u) >= 0); - printf("uid = "UID_FMT"\n", u); - assert_se(u == u2); - - assert_se(sd_session_get_type(session, &type) >= 0); - printf("type = %s\n", type); - free(type); - - assert_se(sd_session_get_class(session, &class) >= 0); - printf("class = %s\n", class); - free(class); - - display = NULL; - r = sd_session_get_display(session, &display); - assert_se(r >= 0 || r == -ENODATA); - printf("display = %s\n", strna(display)); - free(display); - - remote_user = NULL; - r = sd_session_get_remote_user(session, &remote_user); - assert_se(r >= 0 || r == -ENODATA); - printf("remote_user = %s\n", strna(remote_user)); - free(remote_user); - - remote_host = NULL; - r = sd_session_get_remote_host(session, &remote_host); - assert_se(r >= 0 || r == -ENODATA); - printf("remote_host = %s\n", strna(remote_host)); - free(remote_host); - - assert_se(sd_session_get_seat(session, &seat) >= 0); - printf("seat = %s\n", seat); - - r = sd_seat_can_multi_session(seat); - assert_se(r >= 0); - printf("can do multi session = %s\n", yes_no(r)); - - r = sd_seat_can_tty(seat); - assert_se(r >= 0); - printf("can do tty = %s\n", yes_no(r)); - - r = sd_seat_can_graphical(seat); - assert_se(r >= 0); - printf("can do graphical = %s\n", yes_no(r)); - - assert_se(sd_uid_get_state(u, &state) >= 0); - printf("state = %s\n", state); - - assert_se(sd_uid_is_on_seat(u, 0, seat) > 0); - - k = sd_uid_is_on_seat(u, 1, seat); - assert_se(k >= 0); - assert_se(!!r == !!r); - - assert_se(sd_seat_get_active(seat, &session2, &u2) >= 0); - printf("session2 = %s\n", session2); - printf("uid2 = "UID_FMT"\n", u2); - - r = sd_seat_get_sessions(seat, &sessions, &uids, &n); - assert_se(r >= 0); - printf("n_sessions = %i\n", r); - assert_se(r == (int) strv_length(sessions)); - assert_se(t = strv_join(sessions, ", ")); - strv_free(sessions); - printf("sessions = %s\n", t); - free(t); - printf("uids ="); - for (k = 0; k < (int) n; k++) - printf(" "UID_FMT, uids[k]); - printf("\n"); - free(uids); - - assert_se(sd_seat_get_sessions(seat, NULL, NULL, NULL) == r); - - free(session); - free(state); - free(session2); - free(seat); - - r = sd_get_seats(&seats); - assert_se(r >= 0); - assert_se(r == (int) strv_length(seats)); - assert_se(t = strv_join(seats, ", ")); - strv_free(seats); - printf("n_seats = %i\n", r); - printf("seats = %s\n", t); - free(t); - - assert_se(sd_get_seats(NULL) == r); - - r = sd_seat_get_active(NULL, &t, NULL); - assert_se(r >= 0); - printf("active session on current seat = %s\n", t); - free(t); - - r = sd_get_sessions(&sessions); - assert_se(r >= 0); - assert_se(r == (int) strv_length(sessions)); - assert_se(t = strv_join(sessions, ", ")); - strv_free(sessions); - printf("n_sessions = %i\n", r); - printf("sessions = %s\n", t); - free(t); - - assert_se(sd_get_sessions(NULL) == r); - - r = sd_get_uids(&uids); - assert_se(r >= 0); - - printf("uids ="); - for (k = 0; k < r; k++) - printf(" "UID_FMT, uids[k]); - printf("\n"); - free(uids); - - printf("n_uids = %i\n", r); - assert_se(sd_get_uids(NULL) == r); - - r = sd_get_machine_names(&machines); - assert_se(r >= 0); - assert_se(r == (int) strv_length(machines)); - assert_se(t = strv_join(machines, ", ")); - strv_free(machines); - printf("n_machines = %i\n", r); - printf("machines = %s\n", t); - free(t); - - r = sd_login_monitor_new("session", &m); - assert_se(r >= 0); - - for (n = 0; n < 5; n++) { - usec_t timeout, nw; - - zero(pollfd); - assert_se((pollfd.fd = sd_login_monitor_get_fd(m)) >= 0); - assert_se((pollfd.events = sd_login_monitor_get_events(m)) >= 0); - - assert_se(sd_login_monitor_get_timeout(m, &timeout) >= 0); - - nw = now(CLOCK_MONOTONIC); - - r = poll(&pollfd, 1, - timeout == (uint64_t) -1 ? -1 : - timeout > nw ? (int) ((timeout - nw) / 1000) : - 0); - - assert_se(r >= 0); - - sd_login_monitor_flush(m); - printf("Wake!\n"); - } - - sd_login_monitor_unref(m); -} - -int main(int argc, char* argv[]) { - log_parse_environment(); - log_open(); - - test_login(); - - return 0; -} diff --git a/src/libsystemd/sd-netlink/Makefile b/src/libsystemd/sd-netlink/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-netlink/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-netlink/local-addresses.c b/src/libsystemd/sd-netlink/local-addresses.c deleted file mode 100644 index ed9ee041ab..0000000000 --- a/src/libsystemd/sd-netlink/local-addresses.c +++ /dev/null @@ -1,275 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2011 Lennart Poettering - Copyright 2014 Tom Gundersen - - 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 . -***/ - -#include "sd-netlink.h" - -#include "alloc-util.h" -#include "local-addresses.h" -#include "macro.h" -#include "netlink-util.h" - -static int address_compare(const void *_a, const void *_b) { - const struct local_address *a = _a, *b = _b; - - /* Order lowest scope first, IPv4 before IPv6, lowest interface index first */ - - if (a->family == AF_INET && b->family == AF_INET6) - return -1; - if (a->family == AF_INET6 && b->family == AF_INET) - return 1; - - if (a->scope < b->scope) - return -1; - if (a->scope > b->scope) - return 1; - - if (a->metric < b->metric) - return -1; - if (a->metric > b->metric) - return 1; - - if (a->ifindex < b->ifindex) - return -1; - if (a->ifindex > b->ifindex) - return 1; - - return memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(a->family)); -} - -int local_addresses(sd_netlink *context, int ifindex, int af, struct local_address **ret) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - _cleanup_free_ struct local_address *list = NULL; - size_t n_list = 0, n_allocated = 0; - sd_netlink_message *m; - int r; - - assert(ret); - - if (context) - rtnl = sd_netlink_ref(context); - else { - r = sd_netlink_open(&rtnl); - if (r < 0) - return r; - } - - r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, af); - if (r < 0) - return r; - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (m = reply; m; m = sd_netlink_message_next(m)) { - struct local_address *a; - unsigned char flags; - uint16_t type; - int ifi, family; - - r = sd_netlink_message_get_errno(m); - if (r < 0) - return r; - - r = sd_netlink_message_get_type(m, &type); - if (r < 0) - return r; - if (type != RTM_NEWADDR) - continue; - - r = sd_rtnl_message_addr_get_ifindex(m, &ifi); - if (r < 0) - return r; - if (ifindex > 0 && ifi != ifindex) - continue; - - r = sd_rtnl_message_addr_get_family(m, &family); - if (r < 0) - return r; - if (af != AF_UNSPEC && af != family) - continue; - - r = sd_rtnl_message_addr_get_flags(m, &flags); - if (r < 0) - return r; - if (flags & IFA_F_DEPRECATED) - continue; - - if (!GREEDY_REALLOC0(list, n_allocated, n_list+1)) - return -ENOMEM; - - a = list + n_list; - - r = sd_rtnl_message_addr_get_scope(m, &a->scope); - if (r < 0) - return r; - - if (ifindex == 0 && (a->scope == RT_SCOPE_HOST || a->scope == RT_SCOPE_NOWHERE)) - continue; - - switch (family) { - - case AF_INET: - r = sd_netlink_message_read_in_addr(m, IFA_LOCAL, &a->address.in); - if (r < 0) { - r = sd_netlink_message_read_in_addr(m, IFA_ADDRESS, &a->address.in); - if (r < 0) - continue; - } - break; - - case AF_INET6: - r = sd_netlink_message_read_in6_addr(m, IFA_LOCAL, &a->address.in6); - if (r < 0) { - r = sd_netlink_message_read_in6_addr(m, IFA_ADDRESS, &a->address.in6); - if (r < 0) - continue; - } - break; - - default: - continue; - } - - a->ifindex = ifi; - a->family = family; - - n_list++; - }; - - qsort_safe(list, n_list, sizeof(struct local_address), address_compare); - - *ret = list; - list = NULL; - - return (int) n_list; -} - -int local_gateways(sd_netlink *context, int ifindex, int af, struct local_address **ret) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - _cleanup_free_ struct local_address *list = NULL; - sd_netlink_message *m = NULL; - size_t n_list = 0, n_allocated = 0; - int r; - - assert(ret); - - if (context) - rtnl = sd_netlink_ref(context); - else { - r = sd_netlink_open(&rtnl); - if (r < 0) - return r; - } - - r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, af, RTPROT_UNSPEC); - if (r < 0) - return r; - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return r; - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (m = reply; m; m = sd_netlink_message_next(m)) { - struct local_address *a; - uint16_t type; - unsigned char dst_len, src_len; - uint32_t ifi; - int family; - - r = sd_netlink_message_get_errno(m); - if (r < 0) - return r; - - r = sd_netlink_message_get_type(m, &type); - if (r < 0) - return r; - if (type != RTM_NEWROUTE) - continue; - - /* We only care for default routes */ - r = sd_rtnl_message_route_get_dst_prefixlen(m, &dst_len); - if (r < 0) - return r; - if (dst_len != 0) - continue; - - r = sd_rtnl_message_route_get_src_prefixlen(m, &src_len); - if (r < 0) - return r; - if (src_len != 0) - continue; - - r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi); - if (r < 0) - return r; - if (ifindex > 0 && (int) ifi != ifindex) - continue; - - r = sd_rtnl_message_route_get_family(m, &family); - if (r < 0) - return r; - if (af != AF_UNSPEC && af != family) - continue; - - if (!GREEDY_REALLOC0(list, n_allocated, n_list + 1)) - return -ENOMEM; - - a = list + n_list; - - switch (family) { - case AF_INET: - r = sd_netlink_message_read_in_addr(m, RTA_GATEWAY, &a->address.in); - if (r < 0) - continue; - - break; - case AF_INET6: - r = sd_netlink_message_read_in6_addr(m, RTA_GATEWAY, &a->address.in6); - if (r < 0) - continue; - - break; - default: - continue; - } - - sd_netlink_message_read_u32(m, RTA_PRIORITY, &a->metric); - - a->ifindex = ifi; - a->family = family; - - n_list++; - } - - if (n_list > 0) - qsort(list, n_list, sizeof(struct local_address), address_compare); - - *ret = list; - list = NULL; - - return (int) n_list; -} diff --git a/src/libsystemd/sd-netlink/local-addresses.h b/src/libsystemd/sd-netlink/local-addresses.h deleted file mode 100644 index 18d71e797e..0000000000 --- a/src/libsystemd/sd-netlink/local-addresses.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2008-2011 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 . -***/ - - -#include "sd-netlink.h" - -#include "in-addr-util.h" - -struct local_address { - int family, ifindex; - unsigned char scope; - uint32_t metric; - union in_addr_union address; -}; - -int local_addresses(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret); - -int local_gateways(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret); diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h deleted file mode 100644 index dcfb080ad3..0000000000 --- a/src/libsystemd/sd-netlink/netlink-internal.h +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Tom Gundersen - - 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 . -***/ - -#include - -#include "sd-netlink.h" - -#include "list.h" -#include "netlink-types.h" -#include "prioq.h" -#include "refcnt.h" - -#define RTNL_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC)) - -#define RTNL_WQUEUE_MAX 1024 -#define RTNL_RQUEUE_MAX 64*1024 - -#define RTNL_CONTAINER_DEPTH 32 - -struct reply_callback { - sd_netlink_message_handler_t callback; - void *userdata; - usec_t timeout; - uint64_t serial; - unsigned prioq_idx; -}; - -struct match_callback { - sd_netlink_message_handler_t callback; - uint16_t type; - void *userdata; - - LIST_FIELDS(struct match_callback, match_callbacks); -}; - -struct sd_netlink { - RefCount n_ref; - - int fd; - - union { - struct sockaddr sa; - struct sockaddr_nl nl; - } sockaddr; - - Hashmap *broadcast_group_refs; - bool broadcast_group_dont_leave:1; /* until we can rely on 4.2 */ - - sd_netlink_message **rqueue; - unsigned rqueue_size; - size_t rqueue_allocated; - - sd_netlink_message **rqueue_partial; - unsigned rqueue_partial_size; - size_t rqueue_partial_allocated; - - struct nlmsghdr *rbuffer; - size_t rbuffer_allocated; - - 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; -}; - -struct netlink_attribute { - size_t offset; /* offset from hdr to attribute */ - bool nested:1; - bool net_byteorder:1; -}; - -struct netlink_container { - const struct NLTypeSystem *type_system; /* the type system of the container */ - size_t offset; /* offset from hdr to the start of the container */ - struct netlink_attribute *attributes; - unsigned short n_attributes; /* number of attributes in container */ -}; - -struct sd_netlink_message { - RefCount n_ref; - - sd_netlink *rtnl; - - struct nlmsghdr *hdr; - struct netlink_container containers[RTNL_CONTAINER_DEPTH]; - unsigned n_containers; /* number of containers */ - bool sealed:1; - bool broadcast:1; - - sd_netlink_message *next; /* next in a chain of multi-part messages */ -}; - -int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type); -int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret); - -int socket_open(int family); -int socket_bind(sd_netlink *nl); -int socket_broadcast_group_ref(sd_netlink *nl, unsigned group); -int socket_broadcast_group_unref(sd_netlink *nl, unsigned group); -int socket_write_message(sd_netlink *nl, sd_netlink_message *m); -int socket_read_message(sd_netlink *nl); - -int rtnl_rqueue_make_room(sd_netlink *rtnl); -int rtnl_rqueue_partial_make_room(sd_netlink *rtnl); - -/* Make sure callbacks don't destroy the rtnl connection */ -#define NETLINK_DONT_DESTROY(rtnl) \ - _cleanup_(sd_netlink_unrefp) _unused_ sd_netlink *_dont_destroy_##rtnl = sd_netlink_ref(rtnl) diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c deleted file mode 100644 index 86d8dee867..0000000000 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ /dev/null @@ -1,961 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Tom Gundersen - - 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 . -***/ - -#include -#include -#include - -#include "sd-netlink.h" - -#include "alloc-util.h" -#include "formats-util.h" -#include "missing.h" -#include "netlink-internal.h" -#include "netlink-types.h" -#include "netlink-util.h" -#include "refcnt.h" -#include "socket-util.h" -#include "util.h" - -#define GET_CONTAINER(m, i) ((i) < (m)->n_containers ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset) : NULL) -#define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr; - -#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK) -#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK) - -int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) { - sd_netlink_message *m; - - assert_return(ret, -EINVAL); - - /* Note that 'rtnl' is currently unused, if we start using it internally - we must take care to avoid problems due to mutual references between - buses and their queued messages. See sd-bus. - */ - - m = new0(sd_netlink_message, 1); - if (!m) - return -ENOMEM; - - m->n_ref = REFCNT_INIT; - - m->sealed = false; - - *ret = m; - - return 0; -} - -int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - const NLType *nl_type; - size_t size; - int r; - - r = type_system_get_type(&type_system_root, &nl_type, type); - if (r < 0) - return r; - - if (type_get_type(nl_type) != NETLINK_TYPE_NESTED) - return -EINVAL; - - r = message_new_empty(rtnl, &m); - if (r < 0) - return r; - - size = NLMSG_SPACE(type_get_size(nl_type)); - - assert(size >= sizeof(struct nlmsghdr)); - m->hdr = malloc0(size); - if (!m->hdr) - return -ENOMEM; - - m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - - type_get_type_system(nl_type, &m->containers[0].type_system); - m->hdr->nlmsg_len = size; - m->hdr->nlmsg_type = type; - - *ret = m; - m = NULL; - - return 0; -} - -int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(m->hdr->nlmsg_type == RTM_GETLINK || - m->hdr->nlmsg_type == RTM_GETADDR || - m->hdr->nlmsg_type == RTM_GETROUTE || - m->hdr->nlmsg_type == RTM_GETNEIGH, - -EINVAL); - - SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump); - - return 0; -} - -sd_netlink_message *sd_netlink_message_ref(sd_netlink_message *m) { - if (m) - assert_se(REFCNT_INC(m->n_ref) >= 2); - - return m; -} - -sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m) { - if (m && REFCNT_DEC(m->n_ref) == 0) { - unsigned i; - - free(m->hdr); - - for (i = 0; i <= m->n_containers; i++) - free(m->containers[i].attributes); - - sd_netlink_message_unref(m->next); - - free(m); - } - - return NULL; -} - -int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) { - assert_return(m, -EINVAL); - assert_return(type, -EINVAL); - - *type = m->hdr->nlmsg_type; - - return 0; -} - -int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags) { - assert_return(m, -EINVAL); - assert_return(flags, -EINVAL); - - m->hdr->nlmsg_flags = flags; - - return 0; -} - -int sd_netlink_message_is_broadcast(sd_netlink_message *m) { - assert_return(m, -EINVAL); - - return m->broadcast; -} - -/* If successful the updated message will be correctly aligned, if - unsuccessful the old message is untouched. */ -static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *data, size_t data_length) { - uint32_t rta_length; - size_t message_length, padding_length; - struct nlmsghdr *new_hdr; - struct rtattr *rta; - char *padding; - unsigned i; - int offset; - - assert(m); - assert(m->hdr); - assert(!m->sealed); - assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len); - assert(!data || data_length); - - /* get offset of the new attribute */ - offset = m->hdr->nlmsg_len; - - /* 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 = offset + 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 + offset); - - /* if we are inside containers, extend them */ - for (i = 0; i < m->n_containers; i++) - GET_CONTAINER(m, i)->rta_len += message_length - offset; - - /* fill in the attribute */ - rta->rta_type = type; - rta->rta_len = rta_length; - if (data) - /* 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); - - else - /* if no data was passed, make sure we still initialize the padding - note that we can have data_length > 0 (used by some containers) */ - padding = RTA_DATA(rta); - - /* make sure also the padding at the end of the message is initialized */ - padding_length = (uint8_t*)m->hdr + message_length - (uint8_t*)padding; - memzero(padding, padding_length); - - /* update message size */ - m->hdr->nlmsg_len = message_length; - - return offset; -} - -static int message_attribute_has_type(sd_netlink_message *m, size_t *out_size, uint16_t attribute_type, uint16_t data_type) { - const NLType *type; - int r; - - assert(m); - - r = type_system_get_type(m->containers[m->n_containers].type_system, &type, attribute_type); - if (r < 0) - return r; - - if (type_get_type(type) != data_type) - return -EINVAL; - - if (out_size) - *out_size = type_get_size(type); - return 0; -} - -int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data) { - size_t length, size; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(data, -EINVAL); - - r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING); - if (r < 0) - return r; - - if (size) { - length = strnlen(data, size+1); - if (length > size) - return -EINVAL; - } else - length = strlen(data); - - r = add_rtattr(m, type, data, length + 1); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type) { - size_t size; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_FLAG); - if (r < 0) - return r; - - r = add_rtattr(m, type, NULL, 0); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8); - if (r < 0) - return r; - - r = add_rtattr(m, type, &data, sizeof(uint8_t)); - if (r < 0) - return r; - - return 0; -} - - -int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16); - if (r < 0) - return r; - - r = add_rtattr(m, type, &data, sizeof(uint16_t)); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32); - if (r < 0) - return r; - - r = add_rtattr(m, type, &data, sizeof(uint32_t)); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - r = add_rtattr(m, type, data, len); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(data, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); - if (r < 0) - return r; - - r = add_rtattr(m, type, data, sizeof(struct in_addr)); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(data, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); - if (r < 0) - return r; - - r = add_rtattr(m, type, data, sizeof(struct in6_addr)); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(data, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); - if (r < 0) - return r; - - r = add_rtattr(m, type, data, ETH_ALEN); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(info, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO); - if (r < 0) - return r; - - r = add_rtattr(m, type, info, sizeof(struct ifa_cacheinfo)); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type) { - size_t size; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -ERANGE); - - r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_NESTED); - if (r < 0) { - const NLTypeSystemUnion *type_system_union; - int family; - - r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_UNION); - if (r < 0) - return r; - - r = sd_rtnl_message_get_family(m, &family); - if (r < 0) - return r; - - r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type); - if (r < 0) - return r; - - r = type_system_union_protocol_get_type_system(type_system_union, - &m->containers[m->n_containers + 1].type_system, - family); - if (r < 0) - return r; - } else { - r = type_system_get_type_system(m->containers[m->n_containers].type_system, - &m->containers[m->n_containers + 1].type_system, - type); - if (r < 0) - return r; - } - - r = add_rtattr(m, type | NLA_F_NESTED, NULL, size); - if (r < 0) - return r; - - m->containers[m->n_containers++].offset = r; - - return 0; -} - -int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key) { - const NLTypeSystemUnion *type_system_union; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type); - if (r < 0) - return r; - - r = type_system_union_get_type_system(type_system_union, - &m->containers[m->n_containers + 1].type_system, - key); - if (r < 0) - return r; - - r = sd_netlink_message_append_string(m, type_system_union->match, key); - if (r < 0) - return r; - - /* do we evere need non-null size */ - r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0); - if (r < 0) - return r; - - m->containers[m->n_containers++].offset = r; - - return 0; -} - - -int sd_netlink_message_close_container(sd_netlink_message *m) { - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(m->n_containers > 0, -EINVAL); - - m->containers[m->n_containers].type_system = NULL; - m->n_containers--; - - return 0; -} - -static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data, bool *net_byteorder) { - struct netlink_attribute *attribute; - struct rtattr *rta; - - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - assert_return(data, -EINVAL); - assert(m->n_containers < RTNL_CONTAINER_DEPTH); - assert(m->containers[m->n_containers].attributes); - assert(type < m->containers[m->n_containers].n_attributes); - - attribute = &m->containers[m->n_containers].attributes[type]; - - if (!attribute->offset) - return -ENODATA; - - rta = (struct rtattr*)((uint8_t *) m->hdr + attribute->offset); - - *data = RTA_DATA(rta); - - if (net_byteorder) - *net_byteorder = attribute->net_byteorder; - - return RTA_PAYLOAD(rta); -} - -int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) { - int r; - void *attr_data; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if (strnlen(attr_data, r) >= (size_t) r) - return -EIO; - - if (data) - *data = (const char *) attr_data; - - return 0; -} - -int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) { - int r; - void *attr_data; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if ((size_t) r < sizeof(uint8_t)) - return -EIO; - - if (data) - *data = *(uint8_t *) attr_data; - - return 0; -} - -int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) { - void *attr_data; - bool net_byteorder; - int r; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); - if (r < 0) - return r; - else if ((size_t) r < sizeof(uint16_t)) - return -EIO; - - if (data) { - if (net_byteorder) - *data = be16toh(*(uint16_t *) attr_data); - else - *data = *(uint16_t *) attr_data; - } - - return 0; -} - -int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) { - void *attr_data; - bool net_byteorder; - int r; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); - if (r < 0) - return r; - else if ((size_t)r < sizeof(uint32_t)) - return -EIO; - - if (data) { - if (net_byteorder) - *data = be32toh(*(uint32_t *) attr_data); - else - *data = *(uint32_t *) attr_data; - } - - return 0; -} - -int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) { - int r; - void *attr_data; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if ((size_t)r < sizeof(struct ether_addr)) - return -EIO; - - if (data) - memcpy(data, attr_data, sizeof(struct ether_addr)); - - return 0; -} - -int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) { - int r; - void *attr_data; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if ((size_t)r < sizeof(struct ifa_cacheinfo)) - return -EIO; - - if (info) - memcpy(info, attr_data, sizeof(struct ifa_cacheinfo)); - - return 0; -} - -int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) { - int r; - void *attr_data; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if ((size_t)r < sizeof(struct in_addr)) - return -EIO; - - if (data) - memcpy(data, attr_data, sizeof(struct in_addr)); - - return 0; -} - -int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) { - int r; - void *attr_data; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if ((size_t)r < sizeof(struct in6_addr)) - return -EIO; - - if (data) - memcpy(data, attr_data, sizeof(struct in6_addr)); - - return 0; -} - -static int netlink_container_parse(sd_netlink_message *m, - struct netlink_container *container, - int count, - struct rtattr *rta, - unsigned int rt_len) { - _cleanup_free_ struct netlink_attribute *attributes = NULL; - - attributes = new0(struct netlink_attribute, count); - if (!attributes) - return -ENOMEM; - - for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) { - unsigned short type; - - type = RTA_TYPE(rta); - - /* if the kernel is newer than the headers we used - when building, we ignore out-of-range attributes */ - if (type >= count) - continue; - - if (attributes[type].offset) - log_debug("rtnl: message parse - overwriting repeated attribute"); - - attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr; - attributes[type].nested = RTA_FLAGS(rta) & NLA_F_NESTED; - attributes[type].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER; - } - - container->attributes = attributes; - attributes = NULL; - container->n_attributes = count; - - return 0; -} - -int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type_id) { - const NLType *nl_type; - const NLTypeSystem *type_system; - void *container; - uint16_t type; - size_t size; - int r; - - assert_return(m, -EINVAL); - assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -EINVAL); - - r = type_system_get_type(m->containers[m->n_containers].type_system, - &nl_type, - type_id); - if (r < 0) - return r; - - type = type_get_type(nl_type); - - if (type == NETLINK_TYPE_NESTED) { - r = type_system_get_type_system(m->containers[m->n_containers].type_system, - &type_system, - type_id); - if (r < 0) - return r; - } else if (type == NETLINK_TYPE_UNION) { - const NLTypeSystemUnion *type_system_union; - - r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, - &type_system_union, - type_id); - if (r < 0) - return r; - - switch (type_system_union->match_type) { - case NL_MATCH_SIBLING: - { - const char *key; - - r = sd_netlink_message_read_string(m, type_system_union->match, &key); - if (r < 0) - return r; - - r = type_system_union_get_type_system(type_system_union, - &type_system, - key); - if (r < 0) - return r; - - break; - } - case NL_MATCH_PROTOCOL: - { - int family; - - r = sd_rtnl_message_get_family(m, &family); - if (r < 0) - return r; - - r = type_system_union_protocol_get_type_system(type_system_union, - &type_system, - family); - if (r < 0) - return r; - - break; - } - default: - assert_not_reached("sd-netlink: invalid type system union type"); - } - } else - return -EINVAL; - - r = netlink_message_read_internal(m, type_id, &container, NULL); - if (r < 0) - return r; - else - size = (size_t)r; - - m->n_containers++; - - r = netlink_container_parse(m, - &m->containers[m->n_containers], - type_system_get_count(type_system), - container, - size); - if (r < 0) { - m->n_containers--; - return r; - } - - m->containers[m->n_containers].type_system = type_system; - - return 0; -} - -int sd_netlink_message_exit_container(sd_netlink_message *m) { - assert_return(m, -EINVAL); - assert_return(m->sealed, -EINVAL); - assert_return(m->n_containers > 0, -EINVAL); - - m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes); - m->containers[m->n_containers].type_system = NULL; - - m->n_containers--; - - return 0; -} - -uint32_t rtnl_message_get_serial(sd_netlink_message *m) { - assert(m); - assert(m->hdr); - - return m->hdr->nlmsg_seq; -} - -int sd_netlink_message_is_error(sd_netlink_message *m) { - assert_return(m, 0); - assert_return(m->hdr, 0); - - return m->hdr->nlmsg_type == NLMSG_ERROR; -} - -int sd_netlink_message_get_errno(sd_netlink_message *m) { - struct nlmsgerr *err; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - - if (!sd_netlink_message_is_error(m)) - return 0; - - err = NLMSG_DATA(m->hdr); - - return err->error; -} - -int sd_netlink_message_rewind(sd_netlink_message *m) { - const NLType *nl_type; - uint16_t type; - size_t size; - unsigned i; - int r; - - assert_return(m, -EINVAL); - - /* don't allow appending to message once parsed */ - if (!m->sealed) - rtnl_message_seal(m); - - for (i = 1; i <= m->n_containers; i++) - m->containers[i].attributes = mfree(m->containers[i].attributes); - - m->n_containers = 0; - - if (m->containers[0].attributes) - /* top-level attributes have already been parsed */ - return 0; - - assert(m->hdr); - - r = type_system_get_type(&type_system_root, &nl_type, m->hdr->nlmsg_type); - if (r < 0) - return r; - - type = type_get_type(nl_type); - size = type_get_size(nl_type); - - if (type == NETLINK_TYPE_NESTED) { - const NLTypeSystem *type_system; - - type_get_type_system(nl_type, &type_system); - - m->containers[0].type_system = type_system; - - r = netlink_container_parse(m, - &m->containers[m->n_containers], - type_system_get_count(type_system), - (struct rtattr*)((uint8_t*)NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)), - NLMSG_PAYLOAD(m->hdr, size)); - if (r < 0) - return r; - } - - return 0; -} - -void rtnl_message_seal(sd_netlink_message *m) { - assert(m); - assert(!m->sealed); - - m->sealed = true; -} - -sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m) { - assert_return(m, NULL); - - return m->next; -} diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c deleted file mode 100644 index c165fa3359..0000000000 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ /dev/null @@ -1,474 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Tom Gundersen - - 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 . -***/ - -#include -#include -#include - -#include "sd-netlink.h" - -#include "alloc-util.h" -#include "formats-util.h" -#include "missing.h" -#include "netlink-internal.h" -#include "netlink-types.h" -#include "netlink-util.h" -#include "refcnt.h" -#include "socket-util.h" -#include "util.h" - -int socket_open(int family) { - int fd; - - fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family); - if (fd < 0) - return -errno; - - return fd; -} - -static int broadcast_groups_get(sd_netlink *nl) { - _cleanup_free_ uint32_t *groups = NULL; - socklen_t len = 0, old_len; - unsigned i, j; - int r; - - assert(nl); - assert(nl->fd >= 0); - - r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len); - if (r < 0) { - if (errno == ENOPROTOOPT) { - nl->broadcast_group_dont_leave = true; - return 0; - } else - return -errno; - } - - if (len == 0) - return 0; - - groups = new0(uint32_t, len); - if (!groups) - return -ENOMEM; - - old_len = len; - - r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, groups, &len); - if (r < 0) - return -errno; - - if (old_len != len) - return -EIO; - - r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL); - if (r < 0) - return r; - - for (i = 0; i < len; i++) { - for (j = 0; j < sizeof(uint32_t) * 8; j++) { - uint32_t offset; - unsigned group; - - offset = 1U << j; - - if (!(groups[i] & offset)) - continue; - - group = i * sizeof(uint32_t) * 8 + j + 1; - - r = hashmap_put(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(1)); - if (r < 0) - return r; - } - } - - return 0; -} - -int socket_bind(sd_netlink *nl) { - socklen_t addrlen; - int r, one = 1; - - r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one)); - if (r < 0) - return -errno; - - addrlen = sizeof(nl->sockaddr); - - r = bind(nl->fd, &nl->sockaddr.sa, addrlen); - /* ignore EINVAL to allow opening an already bound socket */ - if (r < 0 && errno != EINVAL) - return -errno; - - r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen); - if (r < 0) - return -errno; - - r = broadcast_groups_get(nl); - if (r < 0) - return r; - - return 0; -} - -static unsigned broadcast_group_get_ref(sd_netlink *nl, unsigned group) { - assert(nl); - - return PTR_TO_UINT(hashmap_get(nl->broadcast_group_refs, UINT_TO_PTR(group))); -} - -static int broadcast_group_set_ref(sd_netlink *nl, unsigned group, unsigned n_ref) { - int r; - - assert(nl); - - r = hashmap_replace(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(n_ref)); - if (r < 0) - return r; - - return 0; -} - -static int broadcast_group_join(sd_netlink *nl, unsigned group) { - int r; - - assert(nl); - assert(nl->fd >= 0); - assert(group > 0); - - r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); - if (r < 0) - return -errno; - - return 0; -} - -int socket_broadcast_group_ref(sd_netlink *nl, unsigned group) { - unsigned n_ref; - int r; - - assert(nl); - - n_ref = broadcast_group_get_ref(nl, group); - - n_ref++; - - r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL); - if (r < 0) - return r; - - r = broadcast_group_set_ref(nl, group, n_ref); - if (r < 0) - return r; - - if (n_ref > 1) - /* not yet in the group */ - return 0; - - r = broadcast_group_join(nl, group); - if (r < 0) - return r; - - return 0; -} - -static int broadcast_group_leave(sd_netlink *nl, unsigned group) { - int r; - - assert(nl); - assert(nl->fd >= 0); - assert(group > 0); - - if (nl->broadcast_group_dont_leave) - return 0; - - r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group)); - if (r < 0) - return -errno; - - return 0; -} - -int socket_broadcast_group_unref(sd_netlink *nl, unsigned group) { - unsigned n_ref; - int r; - - assert(nl); - - n_ref = broadcast_group_get_ref(nl, group); - - assert(n_ref > 0); - - n_ref--; - - r = broadcast_group_set_ref(nl, group, n_ref); - if (r < 0) - return r; - - if (n_ref > 0) - /* still refs left */ - return 0; - - r = broadcast_group_leave(nl, group); - if (r < 0) - return r; - - return 0; -} - -/* returns the number of bytes sent, or a negative error code */ -int socket_write_message(sd_netlink *nl, sd_netlink_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; - - return k; -} - -static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) { - union sockaddr_union sender; - uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))]; - struct msghdr msg = { - .msg_iov = iov, - .msg_iovlen = 1, - .msg_name = &sender, - .msg_namelen = sizeof(sender), - .msg_control = cmsg_buffer, - .msg_controllen = sizeof(cmsg_buffer), - }; - struct cmsghdr *cmsg; - uint32_t group = 0; - int r; - - assert(fd >= 0); - assert(iov); - - r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0)); - if (r < 0) { - /* no data */ - if (errno == ENOBUFS) - log_debug("rtnl: kernel receive buffer overrun"); - else if (errno == EAGAIN) - log_debug("rtnl: no data in socket"); - - return (errno == EAGAIN || errno == EINTR) ? 0 : -errno; - } - - if (sender.nl.nl_pid != 0) { - /* not from the kernel, ignore */ - log_debug("rtnl: ignoring message from portid %"PRIu32, sender.nl.nl_pid); - - if (peek) { - /* drop the message */ - r = recvmsg(fd, &msg, 0); - if (r < 0) - return (errno == EAGAIN || errno == EINTR) ? 0 : -errno; - } - - return 0; - } - - CMSG_FOREACH(cmsg, &msg) { - if (cmsg->cmsg_level == SOL_NETLINK && - cmsg->cmsg_type == NETLINK_PKTINFO && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) { - struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg); - - /* multi-cast group */ - group = pktinfo->group; - } - } - - if (_group) - *_group = group; - - return r; -} - -/* 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_netlink *rtnl) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL; - struct iovec iov = {}; - uint32_t group = 0; - bool multi_part = false, done = false; - struct nlmsghdr *new_msg; - size_t len; - int r; - unsigned i = 0; - - assert(rtnl); - assert(rtnl->rbuffer); - assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr)); - - /* read nothing, just get the pending message size */ - r = socket_recv_message(rtnl->fd, &iov, NULL, true); - if (r <= 0) - return r; - else - len = (size_t)r; - - /* make room for the pending message */ - if (!greedy_realloc((void **)&rtnl->rbuffer, - &rtnl->rbuffer_allocated, - len, sizeof(uint8_t))) - return -ENOMEM; - - iov.iov_base = rtnl->rbuffer; - iov.iov_len = rtnl->rbuffer_allocated; - - /* read the pending message */ - r = socket_recv_message(rtnl->fd, &iov, &group, false); - if (r <= 0) - return r; - else - len = (size_t)r; - - if (len > rtnl->rbuffer_allocated) - /* message did not fit in read buffer */ - return -EIO; - - if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) { - multi_part = true; - - for (i = 0; i < rtnl->rqueue_partial_size; i++) { - if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) == - rtnl->rbuffer->nlmsg_seq) { - first = rtnl->rqueue_partial[i]; - break; - } - } - } - - for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - const NLType *nl_type; - - if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid) - /* not broadcast and not for us */ - continue; - - if (new_msg->nlmsg_type == NLMSG_NOOP) - /* silently drop noop messages */ - continue; - - if (new_msg->nlmsg_type == NLMSG_DONE) { - /* finished reading multi-part message */ - done = true; - - /* if first is not defined, put NLMSG_DONE into the receive queue. */ - if (first) - continue; - } - - /* check that we support this message type */ - r = type_system_get_type(&type_system_root, &nl_type, new_msg->nlmsg_type); - if (r < 0) { - if (r == -EOPNOTSUPP) - log_debug("sd-netlink: ignored message with unknown type: %i", - new_msg->nlmsg_type); - - continue; - } - - /* check that the size matches the message type */ - if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) { - log_debug("sd-netlink: message larger than expected, dropping"); - continue; - } - - r = message_new_empty(rtnl, &m); - if (r < 0) - return r; - - m->broadcast = !!group; - - m->hdr = memdup(new_msg, new_msg->nlmsg_len); - if (!m->hdr) - return -ENOMEM; - - /* seal and parse the top-level message */ - r = sd_netlink_message_rewind(m); - if (r < 0) - return r; - - /* push the message onto the multi-part message stack */ - if (first) - m->next = first; - first = m; - m = NULL; - } - - if (len) - log_debug("sd-netlink: discarding %zu bytes of incoming message", len); - - if (!first) - return 0; - - if (!multi_part || done) { - /* we got a complete message, push it on the read queue */ - r = rtnl_rqueue_make_room(rtnl); - if (r < 0) - return r; - - rtnl->rqueue[rtnl->rqueue_size++] = first; - first = NULL; - - if (multi_part && (i < rtnl->rqueue_partial_size)) { - /* remove the message form the partial read queue */ - memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1, - sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1)); - rtnl->rqueue_partial_size--; - } - - return 1; - } else { - /* we only got a partial multi-part message, push it on the - partial read queue */ - if (i < rtnl->rqueue_partial_size) { - rtnl->rqueue_partial[i] = first; - } else { - r = rtnl_rqueue_partial_make_room(rtnl); - if (r < 0) - return r; - - rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = first; - } - first = NULL; - - return 0; - } -} diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c deleted file mode 100644 index 3a4bac2ced..0000000000 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ /dev/null @@ -1,684 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen - - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "macro.h" -#include "missing.h" -#include "netlink-types.h" -#include "string-table.h" -#include "util.h" - -/* Maximum ARP IP target defined in kernel */ -#define BOND_MAX_ARP_TARGETS 16 - -typedef enum { - BOND_ARP_TARGETS_0, - BOND_ARP_TARGETS_1, - BOND_ARP_TARGETS_2, - BOND_ARP_TARGETS_3, - BOND_ARP_TARGETS_4, - BOND_ARP_TARGETS_5, - BOND_ARP_TARGETS_6, - BOND_ARP_TARGETS_7, - BOND_ARP_TARGETS_8, - BOND_ARP_TARGETS_9, - BOND_ARP_TARGETS_10, - BOND_ARP_TARGETS_11, - BOND_ARP_TARGETS_12, - BOND_ARP_TARGETS_13, - BOND_ARP_TARGETS_14, - BOND_ARP_TARGETS_MAX = BOND_MAX_ARP_TARGETS, -} BondArpTargets; - -struct NLType { - uint16_t type; - size_t size; - const NLTypeSystem *type_system; - const NLTypeSystemUnion *type_system_union; -}; - -struct NLTypeSystem { - uint16_t count; - const NLType *types; -}; - -static const NLTypeSystem rtnl_link_type_system; - -static const NLType empty_types[1] = { - /* fake array to avoid .types==NULL, which denotes invalid type-systems */ -}; - -static const NLTypeSystem empty_type_system = { - .count = 0, - .types = empty_types, -}; - -static const NLType rtnl_link_info_data_veth_types[] = { - [VETH_INFO_PEER] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, -}; - -static const NLType rtnl_link_info_data_ipvlan_types[] = { - [IFLA_IPVLAN_MODE] = { .type = NETLINK_TYPE_U16 }, -}; - -static const NLType rtnl_link_info_data_macvlan_types[] = { - [IFLA_MACVLAN_MODE] = { .type = NETLINK_TYPE_U32 }, - [IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 }, -}; - -static const NLType rtnl_link_info_data_bridge_types[] = { - [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_AGEING_TIME] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_STP_STATE] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_VLAN_FILTERING] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_ROOT_PORT] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_ROOT_PATH_COST] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_TOPOLOGY_CHANGE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_TOPOLOGY_CHANGE_DETECTED] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_HELLO_TIMER] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_TCN_TIMER] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_TOPOLOGY_CHANGE_TIMER] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_GC_TIMER] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_GROUP_ADDR] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_FDB_FLUSH] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_MCAST_ROUTER] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_MCAST_SNOOPING] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_MCAST_QUERIER] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_MCAST_HASH_MAX] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_NF_CALL_IPTABLES] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NETLINK_TYPE_U16 }, -}; - -static const NLType rtnl_link_info_data_vlan_types[] = { - [IFLA_VLAN_ID] = { .type = NETLINK_TYPE_U16 }, -/* - [IFLA_VLAN_FLAGS] = { .len = sizeof(struct ifla_vlan_flags) }, - [IFLA_VLAN_EGRESS_QOS] = { .type = NETLINK_TYPE_NESTED }, - [IFLA_VLAN_INGRESS_QOS] = { .type = NETLINK_TYPE_NESTED }, -*/ - [IFLA_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 }, -}; - -static const NLType rtnl_link_info_data_vxlan_types[] = { - [IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 }, - [IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 }, - [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_U32}, - [IFLA_VXLAN_TTL] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_TOS] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_LEARNING] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_AGEING] = { .type = NETLINK_TYPE_U32 }, - [IFLA_VXLAN_LIMIT] = { .type = NETLINK_TYPE_U32 }, - [IFLA_VXLAN_PORT_RANGE] = { .type = NETLINK_TYPE_U32}, - [IFLA_VXLAN_PROXY] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_RSC] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_L2MISS] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_L3MISS] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_PORT] = { .type = NETLINK_TYPE_U16 }, - [IFLA_VXLAN_GROUP6] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_VXLAN_LOCAL6] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_VXLAN_UDP_CSUM] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_REMCSUM_TX] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_REMCSUM_RX] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_GBP] = { .type = NETLINK_TYPE_FLAG }, - [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG }, -}; - -static const NLType rtnl_bond_arp_target_types[] = { - [BOND_ARP_TARGETS_0] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_1] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_2] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_3] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_4] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_5] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_6] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_7] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_8] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_9] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_10] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_11] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_12] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_13] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_14] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_MAX] = { .type = NETLINK_TYPE_U32 }, -}; - -static const NLTypeSystem rtnl_bond_arp_type_system = { - .count = ELEMENTSOF(rtnl_bond_arp_target_types), - .types = rtnl_bond_arp_target_types, -}; - -static const NLType rtnl_link_info_data_bond_types[] = { - [IFLA_BOND_MODE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_ACTIVE_SLAVE] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_MIIMON] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_UPDELAY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_DOWNDELAY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_USE_CARRIER] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_ARP_INTERVAL] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_ARP_IP_TARGET] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bond_arp_type_system }, - [IFLA_BOND_ARP_VALIDATE] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_ARP_ALL_TARGETS] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_PRIMARY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_PRIMARY_RESELECT] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_FAIL_OVER_MAC] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_XMIT_HASH_POLICY] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_RESEND_IGMP] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_NUM_PEER_NOTIF] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_ALL_SLAVES_ACTIVE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_MIN_LINKS] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_LP_INTERVAL] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_PACKETS_PER_SLAVE] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_AD_LACP_RATE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_AD_SELECT] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_AD_INFO] = { .type = NETLINK_TYPE_NESTED }, -}; - -static const NLType rtnl_link_info_data_iptun_types[] = { - [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 }, - [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_TOS] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_PMTUDISC] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_FLAGS] = { .type = NETLINK_TYPE_U16 }, - [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_6RD_PREFIX] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_IPTUN_6RD_RELAY_PREFIX] = { .type = NETLINK_TYPE_U32 }, - [IFLA_IPTUN_6RD_PREFIXLEN] = { .type = NETLINK_TYPE_U16 }, - [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NETLINK_TYPE_U16 }, - [IFLA_IPTUN_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 }, - [IFLA_IPTUN_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 }, - [IFLA_IPTUN_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 }, - [IFLA_IPTUN_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, -}; - -static const NLType rtnl_link_info_data_ipgre_types[] = { - [IFLA_GRE_LINK] = { .type = NETLINK_TYPE_U32 }, - [IFLA_GRE_IFLAGS] = { .type = NETLINK_TYPE_U16 }, - [IFLA_GRE_OFLAGS] = { .type = NETLINK_TYPE_U16 }, - [IFLA_GRE_IKEY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_GRE_OKEY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_GRE_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_GRE_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_GRE_TTL] = { .type = NETLINK_TYPE_U8 }, - [IFLA_GRE_TOS] = { .type = NETLINK_TYPE_U8 }, - [IFLA_GRE_PMTUDISC] = { .type = NETLINK_TYPE_U8 }, - [IFLA_GRE_FLOWINFO] = { .type = NETLINK_TYPE_U32 }, - [IFLA_GRE_FLAGS] = { .type = NETLINK_TYPE_U32 }, - [IFLA_GRE_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 }, - [IFLA_GRE_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 }, - [IFLA_GRE_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 }, - [IFLA_GRE_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, -}; - -static const NLType rtnl_link_info_data_ipvti_types[] = { - [IFLA_VTI_LINK] = { .type = NETLINK_TYPE_U32 }, - [IFLA_VTI_IKEY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_VTI_OKEY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_VTI_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_VTI_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, -}; - -static const NLType rtnl_link_info_data_ip6tnl_types[] = { - [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 }, - [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_FLAGS] = { .type = NETLINK_TYPE_U32 }, - [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_FLOWINFO] = { .type = NETLINK_TYPE_U32 }, -}; - -/* these strings must match the .kind entries in the kernel */ -static const char* const nl_union_link_info_data_table[] = { - [NL_UNION_LINK_INFO_DATA_BOND] = "bond", - [NL_UNION_LINK_INFO_DATA_BRIDGE] = "bridge", - [NL_UNION_LINK_INFO_DATA_VLAN] = "vlan", - [NL_UNION_LINK_INFO_DATA_VETH] = "veth", - [NL_UNION_LINK_INFO_DATA_DUMMY] = "dummy", - [NL_UNION_LINK_INFO_DATA_MACVLAN] = "macvlan", - [NL_UNION_LINK_INFO_DATA_MACVTAP] = "macvtap", - [NL_UNION_LINK_INFO_DATA_IPVLAN] = "ipvlan", - [NL_UNION_LINK_INFO_DATA_VXLAN] = "vxlan", - [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = "ipip", - [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = "gre", - [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = "gretap", - [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = "ip6gre", - [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = "ip6gretap", - [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = "sit", - [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = "vti", - [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = "vti6", - [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = "ip6tnl", -}; - -DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData); - -static const NLTypeSystem rtnl_link_info_data_type_systems[] = { - [NL_UNION_LINK_INFO_DATA_BOND] = { .count = ELEMENTSOF(rtnl_link_info_data_bond_types), - .types = rtnl_link_info_data_bond_types }, - [NL_UNION_LINK_INFO_DATA_BRIDGE] = { .count = ELEMENTSOF(rtnl_link_info_data_bridge_types), - .types = rtnl_link_info_data_bridge_types }, - [NL_UNION_LINK_INFO_DATA_VLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vlan_types), - .types = rtnl_link_info_data_vlan_types }, - [NL_UNION_LINK_INFO_DATA_VETH] = { .count = ELEMENTSOF(rtnl_link_info_data_veth_types), - .types = rtnl_link_info_data_veth_types }, - [NL_UNION_LINK_INFO_DATA_MACVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), - .types = rtnl_link_info_data_macvlan_types }, - [NL_UNION_LINK_INFO_DATA_MACVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), - .types = rtnl_link_info_data_macvlan_types }, - [NL_UNION_LINK_INFO_DATA_IPVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types), - .types = rtnl_link_info_data_ipvlan_types }, - [NL_UNION_LINK_INFO_DATA_VXLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types), - .types = rtnl_link_info_data_vxlan_types }, - [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), - .types = rtnl_link_info_data_iptun_types }, - [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), - .types = rtnl_link_info_data_ipgre_types }, - [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), - .types = rtnl_link_info_data_ipgre_types }, - [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), - .types = rtnl_link_info_data_ipgre_types }, - [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), - .types = rtnl_link_info_data_ipgre_types }, - [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), - .types = rtnl_link_info_data_iptun_types }, - [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types), - .types = rtnl_link_info_data_ipvti_types }, - [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types), - .types = rtnl_link_info_data_ipvti_types }, - [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ip6tnl_types), - .types = rtnl_link_info_data_ip6tnl_types }, - -}; - -static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = { - .num = _NL_UNION_LINK_INFO_DATA_MAX, - .lookup = nl_union_link_info_data_from_string, - .type_systems = rtnl_link_info_data_type_systems, - .match_type = NL_MATCH_SIBLING, - .match = IFLA_INFO_KIND, -}; - -static const NLType rtnl_link_info_types[] = { - [IFLA_INFO_KIND] = { .type = NETLINK_TYPE_STRING }, - [IFLA_INFO_DATA] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_link_info_data_type_system_union}, -/* - [IFLA_INFO_XSTATS], - [IFLA_INFO_SLAVE_KIND] = { .type = NETLINK_TYPE_STRING }, - [IFLA_INFO_SLAVE_DATA] = { .type = NETLINK_TYPE_NESTED }, -*/ -}; - -static const NLTypeSystem rtnl_link_info_type_system = { - .count = ELEMENTSOF(rtnl_link_info_types), - .types = rtnl_link_info_types, -}; - -static const struct NLType rtnl_prot_info_bridge_port_types[] = { - [IFLA_BRPORT_STATE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_COST] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BRPORT_PRIORITY] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BRPORT_MODE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_GUARD] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_PROTECT] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_FAST_LEAVE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_LEARNING] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_PROXYARP] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 }, -}; - -static const NLTypeSystem rtnl_prot_info_type_systems[] = { - [AF_BRIDGE] = { .count = ELEMENTSOF(rtnl_prot_info_bridge_port_types), - .types = rtnl_prot_info_bridge_port_types }, -}; - -static const NLTypeSystemUnion rtnl_prot_info_type_system_union = { - .num = AF_MAX, - .type_systems = rtnl_prot_info_type_systems, - .match_type = NL_MATCH_PROTOCOL, -}; - -static const struct NLType rtnl_af_spec_inet6_types[] = { - [IFLA_INET6_FLAGS] = { .type = NETLINK_TYPE_U32 }, -/* - IFLA_INET6_CONF, - IFLA_INET6_STATS, - IFLA_INET6_MCAST, - IFLA_INET6_CACHEINFO, - IFLA_INET6_ICMP6STATS, -*/ - [IFLA_INET6_TOKEN] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_INET6_ADDR_GEN_MODE] = { .type = NETLINK_TYPE_U8 }, -}; - -static const NLTypeSystem rtnl_af_spec_inet6_type_system = { - .count = ELEMENTSOF(rtnl_af_spec_inet6_types), - .types = rtnl_af_spec_inet6_types, -}; - -static const NLType rtnl_af_spec_types[] = { - [AF_INET6] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_inet6_type_system }, -}; - -static const NLTypeSystem rtnl_af_spec_type_system = { - .count = ELEMENTSOF(rtnl_af_spec_types), - .types = rtnl_af_spec_types, -}; - -static const NLType rtnl_link_types[] = { - [IFLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR }, - [IFLA_BROADCAST] = { .type = NETLINK_TYPE_ETHER_ADDR }, - [IFLA_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 }, - [IFLA_MTU] = { .type = NETLINK_TYPE_U32 }, - [IFLA_LINK] = { .type = NETLINK_TYPE_U32 }, -/* - [IFLA_QDISC], - [IFLA_STATS], - [IFLA_COST], - [IFLA_PRIORITY], -*/ - [IFLA_MASTER] = { .type = NETLINK_TYPE_U32 }, -/* - [IFLA_WIRELESS], -*/ - [IFLA_PROTINFO] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_prot_info_type_system_union }, - [IFLA_TXQLEN] = { .type = NETLINK_TYPE_U32 }, -/* - [IFLA_MAP] = { .len = sizeof(struct rtnl_link_ifmap) }, -*/ - [IFLA_WEIGHT] = { .type = NETLINK_TYPE_U32 }, - [IFLA_OPERSTATE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_LINKMODE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_LINKINFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system }, - [IFLA_NET_NS_PID] = { .type = NETLINK_TYPE_U32 }, - [IFLA_IFALIAS] = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 }, -/* - [IFLA_NUM_VF], - [IFLA_VFINFO_LIST] = {. type = NETLINK_TYPE_NESTED, }, - [IFLA_STATS64], - [IFLA_VF_PORTS] = { .type = NETLINK_TYPE_NESTED }, - [IFLA_PORT_SELF] = { .type = NETLINK_TYPE_NESTED }, -*/ - [IFLA_AF_SPEC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_type_system }, -/* - [IFLA_VF_PORTS], - [IFLA_PORT_SELF], - [IFLA_AF_SPEC], -*/ - [IFLA_GROUP] = { .type = NETLINK_TYPE_U32 }, - [IFLA_NET_NS_FD] = { .type = NETLINK_TYPE_U32 }, - [IFLA_EXT_MASK] = { .type = NETLINK_TYPE_U32 }, - [IFLA_PROMISCUITY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_NUM_TX_QUEUES] = { .type = NETLINK_TYPE_U32 }, - [IFLA_NUM_RX_QUEUES] = { .type = NETLINK_TYPE_U32 }, - [IFLA_CARRIER] = { .type = NETLINK_TYPE_U8 }, -/* - [IFLA_PHYS_PORT_ID] = { .type = NETLINK_TYPE_BINARY, .len = MAX_PHYS_PORT_ID_LEN }, -*/ -}; - -static const NLTypeSystem rtnl_link_type_system = { - .count = ELEMENTSOF(rtnl_link_types), - .types = rtnl_link_types, -}; - -/* IFA_FLAGS was defined in kernel 3.14, but we still support older - * kernels where IFA_MAX is lower. */ -static const NLType rtnl_address_types[] = { - [IFA_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFA_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFA_LABEL] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 }, - [IFA_BROADCAST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ - [IFA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct ifa_cacheinfo) }, -/* - [IFA_ANYCAST], - [IFA_MULTICAST], -*/ - [IFA_FLAGS] = { .type = NETLINK_TYPE_U32 }, -}; - -static const NLTypeSystem rtnl_address_type_system = { - .count = ELEMENTSOF(rtnl_address_types), - .types = rtnl_address_types, -}; - -static const NLType rtnl_route_types[] = { - [RTA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ - [RTA_SRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ - [RTA_IIF] = { .type = NETLINK_TYPE_U32 }, - [RTA_OIF] = { .type = NETLINK_TYPE_U32 }, - [RTA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR }, - [RTA_PRIORITY] = { .type = NETLINK_TYPE_U32 }, - [RTA_PREFSRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ -/* - [RTA_METRICS] = { .type = NETLINK_TYPE_NESTED }, - [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, -*/ - [RTA_FLOW] = { .type = NETLINK_TYPE_U32 }, /* 6? */ -/* - RTA_CACHEINFO, - RTA_TABLE, - RTA_MARK, - RTA_MFC_STATS, - RTA_VIA, - RTA_NEWDST, -*/ - [RTA_PREF] = { .type = NETLINK_TYPE_U8 }, - -}; - -static const NLTypeSystem rtnl_route_type_system = { - .count = ELEMENTSOF(rtnl_route_types), - .types = rtnl_route_types, -}; - -static const NLType rtnl_neigh_types[] = { - [NDA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, - [NDA_LLADDR] = { .type = NETLINK_TYPE_ETHER_ADDR }, - [NDA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) }, - [NDA_PROBES] = { .type = NETLINK_TYPE_U32 }, - [NDA_VLAN] = { .type = NETLINK_TYPE_U16 }, - [NDA_PORT] = { .type = NETLINK_TYPE_U16 }, - [NDA_VNI] = { .type = NETLINK_TYPE_U32 }, - [NDA_IFINDEX] = { .type = NETLINK_TYPE_U32 }, -}; - -static const NLTypeSystem rtnl_neigh_type_system = { - .count = ELEMENTSOF(rtnl_neigh_types), - .types = rtnl_neigh_types, -}; - -static const NLType rtnl_types[] = { - [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 }, - [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) }, - [RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, - [RTM_DELLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, - [RTM_GETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, - [RTM_SETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, - [RTM_NEWADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, - [RTM_DELADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, - [RTM_GETADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, - [RTM_NEWROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, - [RTM_DELROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, - [RTM_GETROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, - [RTM_NEWNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, - [RTM_DELNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, - [RTM_GETNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, -}; - -const NLTypeSystem type_system_root = { - .count = ELEMENTSOF(rtnl_types), - .types = rtnl_types, -}; - -uint16_t type_get_type(const NLType *type) { - assert(type); - return type->type; -} - -size_t type_get_size(const NLType *type) { - assert(type); - return type->size; -} - -void type_get_type_system(const NLType *nl_type, const NLTypeSystem **ret) { - assert(nl_type); - assert(ret); - assert(nl_type->type == NETLINK_TYPE_NESTED); - assert(nl_type->type_system); - - *ret = nl_type->type_system; -} - -void type_get_type_system_union(const NLType *nl_type, const NLTypeSystemUnion **ret) { - assert(nl_type); - assert(ret); - assert(nl_type->type == NETLINK_TYPE_UNION); - assert(nl_type->type_system_union); - - *ret = nl_type->type_system_union; -} - -uint16_t type_system_get_count(const NLTypeSystem *type_system) { - assert(type_system); - return type_system->count; -} - -int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type) { - const NLType *nl_type; - - assert(ret); - assert(type_system); - assert(type_system->types); - - if (type >= type_system->count) - return -EOPNOTSUPP; - - nl_type = &type_system->types[type]; - - if (nl_type->type == NETLINK_TYPE_UNSPEC) - return -EOPNOTSUPP; - - *ret = nl_type; - - return 0; -} - -int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type) { - const NLType *nl_type; - int r; - - assert(ret); - - r = type_system_get_type(type_system, &nl_type, type); - if (r < 0) - return r; - - type_get_type_system(nl_type, ret); - return 0; -} - -int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type) { - const NLType *nl_type; - int r; - - assert(ret); - - r = type_system_get_type(type_system, &nl_type, type); - if (r < 0) - return r; - - type_get_type_system_union(nl_type, ret); - return 0; -} - -int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key) { - int type; - - assert(type_system_union); - assert(type_system_union->match_type == NL_MATCH_SIBLING); - assert(type_system_union->lookup); - assert(type_system_union->type_systems); - assert(ret); - assert(key); - - type = type_system_union->lookup(key); - if (type < 0) - return -EOPNOTSUPP; - - assert(type < type_system_union->num); - - *ret = &type_system_union->type_systems[type]; - - return 0; -} - -int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol) { - const NLTypeSystem *type_system; - - assert(type_system_union); - assert(type_system_union->type_systems); - assert(type_system_union->match_type == NL_MATCH_PROTOCOL); - assert(ret); - - if (protocol >= type_system_union->num) - return -EOPNOTSUPP; - - type_system = &type_system_union->type_systems[protocol]; - if (!type_system->types) - return -EOPNOTSUPP; - - *ret = type_system; - - return 0; -} diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h deleted file mode 100644 index ecb20bfcdc..0000000000 --- a/src/libsystemd/sd-netlink/netlink-types.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen - - 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 . -***/ - -#include "macro.h" - -enum { - NETLINK_TYPE_UNSPEC, - NETLINK_TYPE_U8, /* NLA_U8 */ - NETLINK_TYPE_U16, /* NLA_U16 */ - NETLINK_TYPE_U32, /* NLA_U32 */ - NETLINK_TYPE_U64, /* NLA_U64 */ - NETLINK_TYPE_STRING, /* NLA_STRING */ - NETLINK_TYPE_FLAG, /* NLA_FLAG */ - NETLINK_TYPE_IN_ADDR, - NETLINK_TYPE_ETHER_ADDR, - NETLINK_TYPE_CACHE_INFO, - NETLINK_TYPE_NESTED, /* NLA_NESTED */ - NETLINK_TYPE_UNION, -}; - -typedef enum NLMatchType { - NL_MATCH_SIBLING, - NL_MATCH_PROTOCOL, -} NLMatchType; - -typedef struct NLTypeSystemUnion NLTypeSystemUnion; -typedef struct NLTypeSystem NLTypeSystem; -typedef struct NLType NLType; - -struct NLTypeSystemUnion { - int num; - NLMatchType match_type; - uint16_t match; - int (*lookup)(const char *); - const NLTypeSystem *type_systems; -}; - -extern const NLTypeSystem type_system_root; - -uint16_t type_get_type(const NLType *type); -size_t type_get_size(const NLType *type); -void type_get_type_system(const NLType *type, const NLTypeSystem **ret); -void type_get_type_system_union(const NLType *type, const NLTypeSystemUnion **ret); - -uint16_t type_system_get_count(const NLTypeSystem *type_system); -int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type); -int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type); -int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type); -int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key); -int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol); - -typedef enum NLUnionLinkInfoData { - NL_UNION_LINK_INFO_DATA_BOND, - NL_UNION_LINK_INFO_DATA_BRIDGE, - NL_UNION_LINK_INFO_DATA_VLAN, - NL_UNION_LINK_INFO_DATA_VETH, - NL_UNION_LINK_INFO_DATA_DUMMY, - NL_UNION_LINK_INFO_DATA_MACVLAN, - NL_UNION_LINK_INFO_DATA_MACVTAP, - NL_UNION_LINK_INFO_DATA_IPVLAN, - NL_UNION_LINK_INFO_DATA_VXLAN, - NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL, - NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL, - NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL, - NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL, - NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL, - NL_UNION_LINK_INFO_DATA_SIT_TUNNEL, - NL_UNION_LINK_INFO_DATA_VTI_TUNNEL, - NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL, - NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL, - _NL_UNION_LINK_INFO_DATA_MAX, - _NL_UNION_LINK_INFO_DATA_INVALID = -1 -} NLUnionLinkInfoData; - -const char *nl_union_link_info_data_to_string(NLUnionLinkInfoData p) _const_; -NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_; diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c deleted file mode 100644 index 73b9ac0258..0000000000 --- a/src/libsystemd/sd-netlink/netlink-util.c +++ /dev/null @@ -1,170 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 Tom Gundersen - - 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 . -***/ - -#include "sd-netlink.h" - -#include "netlink-internal.h" -#include "netlink-util.h" - -int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; - int r; - - assert(rtnl); - assert(ifindex > 0); - assert(name); - - if (!*rtnl) { - r = sd_netlink_open(rtnl); - if (r < 0) - return r; - } - - r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); - if (r < 0) - return r; - - r = sd_netlink_message_append_string(message, IFLA_IFNAME, name); - if (r < 0) - return r; - - r = sd_netlink_call(*rtnl, message, 0, NULL); - if (r < 0) - return r; - - return 0; -} - -int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, - const struct ether_addr *mac, unsigned mtu) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; - int r; - - assert(rtnl); - assert(ifindex > 0); - - if (!alias && !mac && mtu == 0) - return 0; - - if (!*rtnl) { - r = sd_netlink_open(rtnl); - if (r < 0) - return r; - } - - r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); - if (r < 0) - return r; - - if (alias) { - r = sd_netlink_message_append_string(message, IFLA_IFALIAS, alias); - if (r < 0) - return r; - } - - if (mac) { - r = sd_netlink_message_append_ether_addr(message, IFLA_ADDRESS, mac); - if (r < 0) - return r; - } - - if (mtu > 0) { - r = sd_netlink_message_append_u32(message, IFLA_MTU, mtu); - if (r < 0) - return r; - } - - r = sd_netlink_call(*rtnl, message, 0, NULL); - if (r < 0) - return r; - - return 0; -} - -int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret) { - struct nlmsgerr *err; - int r; - - assert(error <= 0); - - r = message_new(NULL, ret, NLMSG_ERROR); - if (r < 0) - return r; - - (*ret)->hdr->nlmsg_seq = serial; - - err = NLMSG_DATA((*ret)->hdr); - - err->error = error; - - return 0; -} - -bool rtnl_message_type_is_neigh(uint16_t type) { - switch (type) { - case RTM_NEWNEIGH: - case RTM_GETNEIGH: - case RTM_DELNEIGH: - return true; - default: - return false; - } -} - -bool rtnl_message_type_is_route(uint16_t type) { - switch (type) { - case RTM_NEWROUTE: - case RTM_GETROUTE: - case RTM_DELROUTE: - return true; - default: - return false; - } -} - -bool rtnl_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 rtnl_message_type_is_addr(uint16_t type) { - switch (type) { - case RTM_NEWADDR: - case RTM_GETADDR: - case RTM_DELADDR: - return true; - default: - return false; - } -} - -int rtnl_log_parse_error(int r) { - return log_error_errno(r, "Failed to parse netlink message: %m"); -} - -int rtnl_log_create_error(int r) { - return log_error_errno(r, "Failed to create netlink message: %m"); -} diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h deleted file mode 100644 index f49bf4eaa6..0000000000 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2013 Tom Gundersen - - 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 . -***/ - -#include "sd-netlink.h" - -#include "util.h" - -int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret); -uint32_t rtnl_message_get_serial(sd_netlink_message *m); -void rtnl_message_seal(sd_netlink_message *m); - -bool rtnl_message_type_is_link(uint16_t type); -bool rtnl_message_type_is_addr(uint16_t type); -bool rtnl_message_type_is_route(uint16_t type); -bool rtnl_message_type_is_neigh(uint16_t type); - -int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name); -int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, unsigned mtu); - -int rtnl_log_parse_error(int r); -int rtnl_log_create_error(int r); diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c deleted file mode 100644 index 09240c7b2a..0000000000 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ /dev/null @@ -1,702 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Tom Gundersen - - 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 . -***/ - -#include -#include -#include - -#include "sd-netlink.h" - -#include "formats-util.h" -#include "missing.h" -#include "netlink-internal.h" -#include "netlink-types.h" -#include "netlink-util.h" -#include "refcnt.h" -#include "socket-util.h" -#include "util.h" - -int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - if ((rtm->rtm_family == AF_INET && prefixlen > 32) || - (rtm->rtm_family == AF_INET6 && prefixlen > 128)) - return -ERANGE; - - rtm->rtm_dst_len = prefixlen; - - return 0; -} - -int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - if ((rtm->rtm_family == AF_INET && prefixlen > 32) || - (rtm->rtm_family == AF_INET6 && prefixlen > 128)) - return -ERANGE; - - rtm->rtm_src_len = prefixlen; - - return 0; -} - -int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - rtm->rtm_scope = scope; - - return 0; -} - -int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - rtm->rtm_flags = flags; - - return 0; -} - -int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - assert_return(flags, -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - *flags = rtm->rtm_flags; - - return 0; -} - -int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - rtm->rtm_table = table; - - return 0; -} - -int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - assert_return(family, -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - *family = rtm->rtm_family; - - return 0; -} - -int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - rtm->rtm_family = family; - - return 0; -} - -int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - assert_return(protocol, -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - *protocol = rtm->rtm_protocol; - - return 0; -} - -int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - assert_return(scope, -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - *scope = rtm->rtm_scope; - - return 0; -} - -int sd_rtnl_message_route_get_tos(sd_netlink_message *m, unsigned char *tos) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - assert_return(tos, -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - *tos = rtm->rtm_tos; - - return 0; -} - -int sd_rtnl_message_route_get_table(sd_netlink_message *m, unsigned char *table) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - assert_return(table, -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - *table = rtm->rtm_table; - - return 0; -} - -int sd_rtnl_message_route_get_dst_prefixlen(sd_netlink_message *m, unsigned char *dst_len) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - assert_return(dst_len, -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - *dst_len = rtm->rtm_dst_len; - - return 0; -} - -int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len) { - struct rtmsg *rtm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - assert_return(src_len, -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - *src_len = rtm->rtm_src_len; - - return 0; -} - -int sd_rtnl_message_new_route(sd_netlink *rtnl, sd_netlink_message **ret, - uint16_t nlmsg_type, int rtm_family, - unsigned char rtm_protocol) { - struct rtmsg *rtm; - int r; - - assert_return(rtnl_message_type_is_route(nlmsg_type), -EINVAL); - assert_return((nlmsg_type == RTM_GETROUTE && rtm_family == AF_UNSPEC) || - rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL); - assert_return(ret, -EINVAL); - - r = message_new(rtnl, ret, nlmsg_type); - if (r < 0) - return r; - - if (nlmsg_type == RTM_NEWROUTE) - (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND; - - rtm = NLMSG_DATA((*ret)->hdr); - - 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 = rtm_protocol; - - return 0; -} - -int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags) { - struct ndmsg *ndm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); - - ndm = NLMSG_DATA(m->hdr); - ndm->ndm_flags |= flags; - - return 0; -} - -int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state) { - struct ndmsg *ndm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); - - ndm = NLMSG_DATA(m->hdr); - ndm->ndm_state |= state; - - return 0; -} - -int sd_rtnl_message_neigh_get_flags(sd_netlink_message *m, uint8_t *flags) { - struct ndmsg *ndm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); - - ndm = NLMSG_DATA(m->hdr); - *flags = ndm->ndm_flags; - - return 0; -} - -int sd_rtnl_message_neigh_get_state(sd_netlink_message *m, uint16_t *state) { - struct ndmsg *ndm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); - - ndm = NLMSG_DATA(m->hdr); - *state = ndm->ndm_state; - - return 0; -} - -int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family) { - struct ndmsg *ndm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); - assert_return(family, -EINVAL); - - ndm = NLMSG_DATA(m->hdr); - - *family = ndm->ndm_family; - - return 0; -} - -int sd_rtnl_message_neigh_get_ifindex(sd_netlink_message *m, int *index) { - struct ndmsg *ndm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); - assert_return(index, -EINVAL); - - ndm = NLMSG_DATA(m->hdr); - - *index = ndm->ndm_ifindex; - - return 0; -} - -int sd_rtnl_message_new_neigh(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int index, int ndm_family) { - struct ndmsg *ndm; - int r; - - assert_return(rtnl_message_type_is_neigh(nlmsg_type), -EINVAL); - assert_return(ndm_family == AF_INET || - ndm_family == AF_INET6 || - ndm_family == PF_BRIDGE, -EINVAL); - assert_return(ret, -EINVAL); - - r = message_new(rtnl, ret, nlmsg_type); - if (r < 0) - return r; - - if (nlmsg_type == RTM_NEWNEIGH) - (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND; - - ndm = NLMSG_DATA((*ret)->hdr); - - ndm->ndm_family = ndm_family; - ndm->ndm_ifindex = index; - - return 0; -} - -int sd_rtnl_message_link_set_flags(sd_netlink_message *m, unsigned flags, unsigned change) { - struct ifinfomsg *ifi; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); - assert_return(change, -EINVAL); - - ifi = NLMSG_DATA(m->hdr); - - ifi->ifi_flags = flags; - ifi->ifi_change = change; - - return 0; -} - -int sd_rtnl_message_link_set_type(sd_netlink_message *m, unsigned type) { - struct ifinfomsg *ifi; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); - - ifi = NLMSG_DATA(m->hdr); - - ifi->ifi_type = type; - - return 0; -} - -int sd_rtnl_message_link_set_family(sd_netlink_message *m, unsigned family) { - struct ifinfomsg *ifi; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); - - ifi = NLMSG_DATA(m->hdr); - - ifi->ifi_family = family; - - return 0; -} - -int sd_rtnl_message_new_link(sd_netlink *rtnl, sd_netlink_message **ret, - uint16_t nlmsg_type, int index) { - struct ifinfomsg *ifi; - int r; - - assert_return(rtnl_message_type_is_link(nlmsg_type), -EINVAL); - assert_return(ret, -EINVAL); - - r = message_new(rtnl, ret, nlmsg_type); - if (r < 0) - return r; - - if (nlmsg_type == RTM_NEWLINK) - (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; - - ifi = NLMSG_DATA((*ret)->hdr); - - ifi->ifi_family = AF_UNSPEC; - ifi->ifi_index = index; - - return 0; -} - -int sd_rtnl_message_addr_set_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - if ((ifa->ifa_family == AF_INET && prefixlen > 32) || - (ifa->ifa_family == AF_INET6 && prefixlen > 128)) - return -ERANGE; - - ifa->ifa_prefixlen = prefixlen; - - return 0; -} - -int sd_rtnl_message_addr_set_flags(sd_netlink_message *m, unsigned char flags) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - ifa->ifa_flags = flags; - - return 0; -} - -int sd_rtnl_message_addr_set_scope(sd_netlink_message *m, unsigned char scope) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - ifa->ifa_scope = scope; - - return 0; -} - -int sd_rtnl_message_addr_get_family(sd_netlink_message *m, int *family) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - assert_return(family, -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - *family = ifa->ifa_family; - - return 0; -} - -int sd_rtnl_message_addr_get_prefixlen(sd_netlink_message *m, unsigned char *prefixlen) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - assert_return(prefixlen, -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - *prefixlen = ifa->ifa_prefixlen; - - return 0; -} - -int sd_rtnl_message_addr_get_scope(sd_netlink_message *m, unsigned char *scope) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - assert_return(scope, -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - *scope = ifa->ifa_scope; - - return 0; -} - -int sd_rtnl_message_addr_get_flags(sd_netlink_message *m, unsigned char *flags) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - assert_return(flags, -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - *flags = ifa->ifa_flags; - - return 0; -} - -int sd_rtnl_message_addr_get_ifindex(sd_netlink_message *m, int *ifindex) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - assert_return(ifindex, -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - *ifindex = ifa->ifa_index; - - return 0; -} - -int sd_rtnl_message_new_addr(sd_netlink *rtnl, sd_netlink_message **ret, - uint16_t nlmsg_type, int index, - int family) { - struct ifaddrmsg *ifa; - int r; - - assert_return(rtnl_message_type_is_addr(nlmsg_type), -EINVAL); - assert_return((nlmsg_type == RTM_GETADDR && index == 0) || - index > 0, -EINVAL); - assert_return((nlmsg_type == RTM_GETADDR && family == AF_UNSPEC) || - family == AF_INET || family == AF_INET6, -EINVAL); - assert_return(ret, -EINVAL); - - r = message_new(rtnl, ret, nlmsg_type); - if (r < 0) - return r; - - if (nlmsg_type == RTM_GETADDR) - (*ret)->hdr->nlmsg_flags |= NLM_F_DUMP; - - ifa = NLMSG_DATA((*ret)->hdr); - - ifa->ifa_index = index; - ifa->ifa_family = family; - if (family == AF_INET) - ifa->ifa_prefixlen = 32; - else if (family == AF_INET6) - ifa->ifa_prefixlen = 128; - - return 0; -} - -int sd_rtnl_message_new_addr_update(sd_netlink *rtnl, sd_netlink_message **ret, - int index, int family) { - int r; - - r = sd_rtnl_message_new_addr(rtnl, ret, RTM_NEWADDR, index, family); - if (r < 0) - return r; - - (*ret)->hdr->nlmsg_flags |= NLM_F_REPLACE; - - return 0; -} - -int sd_rtnl_message_link_get_ifindex(sd_netlink_message *m, int *ifindex) { - struct ifinfomsg *ifi; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_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_netlink_message *m, unsigned *flags) { - struct ifinfomsg *ifi; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); - assert_return(flags, -EINVAL); - - ifi = NLMSG_DATA(m->hdr); - - *flags = ifi->ifi_flags; - - return 0; -} - -int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned short *type) { - struct ifinfomsg *ifi; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); - assert_return(type, -EINVAL); - - ifi = NLMSG_DATA(m->hdr); - - *type = ifi->ifi_type; - - return 0; -} - -int sd_rtnl_message_get_family(sd_netlink_message *m, int *family) { - assert_return(m, -EINVAL); - assert_return(family, -EINVAL); - - assert(m->hdr); - - if (rtnl_message_type_is_link(m->hdr->nlmsg_type)) { - struct ifinfomsg *ifi; - - ifi = NLMSG_DATA(m->hdr); - - *family = ifi->ifi_family; - - return 0; - } else if (rtnl_message_type_is_route(m->hdr->nlmsg_type)) { - struct rtmsg *rtm; - - rtm = NLMSG_DATA(m->hdr); - - *family = rtm->rtm_family; - - return 0; - } else if (rtnl_message_type_is_neigh(m->hdr->nlmsg_type)) { - struct ndmsg *ndm; - - ndm = NLMSG_DATA(m->hdr); - - *family = ndm->ndm_family; - - return 0; - } else if (rtnl_message_type_is_addr(m->hdr->nlmsg_type)) { - struct ifaddrmsg *ifa; - - ifa = NLMSG_DATA(m->hdr); - - *family = ifa->ifa_family; - - return 0; - } - - return -EOPNOTSUPP; -} diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c deleted file mode 100644 index 91701405a5..0000000000 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ /dev/null @@ -1,954 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Tom Gundersen - - 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 . -***/ - -#include -#include - -#include "sd-netlink.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "hashmap.h" -#include "macro.h" -#include "missing.h" -#include "netlink-internal.h" -#include "netlink-util.h" -#include "socket-util.h" -#include "util.h" - -static int sd_netlink_new(sd_netlink **ret) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - - assert_return(ret, -EINVAL); - - rtnl = new0(sd_netlink, 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 the read buffer has at least space for - * a message header */ - if (!greedy_realloc((void**)&rtnl->rbuffer, &rtnl->rbuffer_allocated, - sizeof(struct nlmsghdr), sizeof(uint8_t))) - return -ENOMEM; - - /* Change notification responses have sequence 0, so we must - * start our request sequence numbers at 1, or we may confuse our - * responses with notifications from the kernel */ - rtnl->serial = 1; - - *ret = rtnl; - rtnl = NULL; - - return 0; -} - -int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - socklen_t addrlen; - int r; - - assert_return(ret, -EINVAL); - - r = sd_netlink_new(&rtnl); - if (r < 0) - return r; - - addrlen = sizeof(rtnl->sockaddr); - - r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen); - if (r < 0) - return -errno; - - if (rtnl->sockaddr.nl.nl_family != AF_NETLINK) - return -EINVAL; - - rtnl->fd = fd; - - *ret = rtnl; - rtnl = NULL; - - return 0; -} - -static bool rtnl_pid_changed(sd_netlink *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_netlink_open_fd(sd_netlink **ret, int fd) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - int r; - - assert_return(ret, -EINVAL); - assert_return(fd >= 0, -EBADF); - - r = sd_netlink_new(&rtnl); - if (r < 0) - return r; - - rtnl->fd = fd; - - r = socket_bind(rtnl); - if (r < 0) { - rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */ - return r; - } - - *ret = rtnl; - rtnl = NULL; - - return 0; -} - -int sd_netlink_open(sd_netlink **ret) { - _cleanup_close_ int fd = -1; - int r; - - fd = socket_open(NETLINK_ROUTE); - if (fd < 0) - return fd; - - r = sd_netlink_open_fd(ret, fd); - if (r < 0) - return r; - - fd = -1; - - return 0; -} - -int sd_netlink_inc_rcvbuf(const sd_netlink *const rtnl, const int size) { - return fd_inc_rcvbuf(rtnl->fd, size); -} - -sd_netlink *sd_netlink_ref(sd_netlink *rtnl) { - assert_return(rtnl, NULL); - assert_return(!rtnl_pid_changed(rtnl), NULL); - - if (rtnl) - assert_se(REFCNT_INC(rtnl->n_ref) >= 2); - - return rtnl; -} - -sd_netlink *sd_netlink_unref(sd_netlink *rtnl) { - if (!rtnl) - return NULL; - - assert_return(!rtnl_pid_changed(rtnl), NULL); - - if (REFCNT_DEC(rtnl->n_ref) == 0) { - struct match_callback *f; - unsigned i; - - for (i = 0; i < rtnl->rqueue_size; i++) - sd_netlink_message_unref(rtnl->rqueue[i]); - free(rtnl->rqueue); - - for (i = 0; i < rtnl->rqueue_partial_size; i++) - sd_netlink_message_unref(rtnl->rqueue_partial[i]); - free(rtnl->rqueue_partial); - - free(rtnl->rbuffer); - - hashmap_free_free(rtnl->reply_callbacks); - prioq_free(rtnl->reply_callbacks_prioq); - - sd_event_source_unref(rtnl->io_event_source); - sd_event_source_unref(rtnl->time_event_source); - sd_event_unref(rtnl->event); - - while ((f = rtnl->match_callbacks)) { - sd_netlink_remove_match(rtnl, f->type, f->callback, f->userdata); - } - - hashmap_free(rtnl->broadcast_group_refs); - - safe_close(rtnl->fd); - free(rtnl); - } - - return NULL; -} - -static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) { - assert(rtnl); - assert(!rtnl_pid_changed(rtnl)); - assert(m); - assert(m->hdr); - - /* don't use seq == 0, as that is used for broadcasts, so we - would get confused by replies to such messages */ - m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++; - - rtnl_message_seal(m); - - return; -} - -int sd_netlink_send(sd_netlink *nl, - sd_netlink_message *message, - uint32_t *serial) { - int r; - - assert_return(nl, -EINVAL); - assert_return(!rtnl_pid_changed(nl), -ECHILD); - assert_return(message, -EINVAL); - assert_return(!message->sealed, -EPERM); - - rtnl_seal_message(nl, message); - - r = socket_write_message(nl, message); - if (r < 0) - return r; - - if (serial) - *serial = rtnl_message_get_serial(message); - - return 1; -} - -int rtnl_rqueue_make_room(sd_netlink *rtnl) { - assert(rtnl); - - if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX) { - log_debug("rtnl: exhausted the read queue size (%d)", RTNL_RQUEUE_MAX); - return -ENOBUFS; - } - - if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_allocated, rtnl->rqueue_size + 1)) - return -ENOMEM; - - return 0; -} - -int rtnl_rqueue_partial_make_room(sd_netlink *rtnl) { - assert(rtnl); - - if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX) { - log_debug("rtnl: exhausted the partial read queue size (%d)", RTNL_RQUEUE_MAX); - return -ENOBUFS; - } - - if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_allocated, - rtnl->rqueue_partial_size + 1)) - return -ENOMEM; - - return 0; -} - -static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) { - int r; - - assert(rtnl); - assert(message); - - if (rtnl->rqueue_size <= 0) { - /* Try to read a new message */ - r = socket_read_message(rtnl); - if (r <= 0) - return r; - } - - /* Dispatch a queued message */ - *message = rtnl->rqueue[0]; - rtnl->rqueue_size--; - memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_netlink_message*) * rtnl->rqueue_size); - - return 1; -} - -static int process_timeout(sd_netlink *rtnl) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_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 = rtnl_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); - if (r < 0) - log_debug_errno(r, "sd-netlink: timedout callback failed: %m"); - - free(c); - - return 1; -} - -static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) { - _cleanup_free_ struct reply_callback *c = NULL; - uint64_t serial; - uint16_t type; - int r; - - assert(rtnl); - assert(m); - - serial = rtnl_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 = sd_netlink_message_get_type(m, &type); - if (r < 0) - return 0; - - if (type == NLMSG_DONE) - m = NULL; - - r = c->callback(rtnl, m, c->userdata); - if (r < 0) - log_debug_errno(r, "sd-netlink: callback failed: %m"); - - return 1; -} - -static int process_match(sd_netlink *rtnl, sd_netlink_message *m) { - struct match_callback *c; - uint16_t type; - int r; - - assert(rtnl); - assert(m); - - r = sd_netlink_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) { - if (r < 0) - log_debug_errno(r, "sd-netlink: match callback failed: %m"); - - break; - } - } - } - - return 1; -} - -static int process_running(sd_netlink *rtnl, sd_netlink_message **ret) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - int r; - - assert(rtnl); - - r = process_timeout(rtnl); - if (r != 0) - goto null_message; - - r = dispatch_rqueue(rtnl, &m); - if (r < 0) - return r; - if (!m) - goto null_message; - - if (sd_netlink_message_is_broadcast(m)) { - r = process_match(rtnl, m); - if (r != 0) - goto null_message; - } else { - r = process_reply(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_netlink_process(sd_netlink *rtnl, sd_netlink_message **ret) { - NETLINK_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_netlink *rtnl, bool need_more, uint64_t timeout_usec) { - struct pollfd p[1] = {}; - struct timespec ts; - usec_t m = USEC_INFINITY; - int r, e; - - assert(rtnl); - - e = sd_netlink_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. */ - e |= POLLIN; - else { - usec_t until; - /* Caller wants to process if there is something to - * process, but doesn't care otherwise */ - - r = sd_netlink_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_netlink_wait(sd_netlink *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_netlink_call_async(sd_netlink *nl, - sd_netlink_message *m, - sd_netlink_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_ops); - 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_netlink_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_netlink_call_async_cancel(nl, c->serial); - return r; - } - } - - if (serial) - *serial = s; - - return k; -} - -int sd_netlink_call_async_cancel(sd_netlink *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_netlink_call(sd_netlink *rtnl, - sd_netlink_message *message, - uint64_t usec, - sd_netlink_message **ret) { - usec_t timeout; - uint32_t serial; - int r; - - assert_return(rtnl, -EINVAL); - assert_return(!rtnl_pid_changed(rtnl), -ECHILD); - assert_return(message, -EINVAL); - - r = sd_netlink_send(rtnl, message, &serial); - if (r < 0) - return r; - - timeout = calc_elapse(usec); - - for (;;) { - usec_t left; - unsigned i; - - for (i = 0; i < rtnl->rqueue_size; i++) { - uint32_t received_serial; - - received_serial = rtnl_message_get_serial(rtnl->rqueue[i]); - - if (received_serial == serial) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *incoming = NULL; - uint16_t type; - - incoming = rtnl->rqueue[i]; - - /* found a match, remove from rqueue and return it */ - memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1, - sizeof(sd_netlink_message*) * (rtnl->rqueue_size - i - 1)); - rtnl->rqueue_size--; - - r = sd_netlink_message_get_errno(incoming); - if (r < 0) - return r; - - r = sd_netlink_message_get_type(incoming, &type); - if (r < 0) - return r; - - if (type == NLMSG_DONE) { - *ret = NULL; - return 0; - } - - if (ret) { - *ret = incoming; - incoming = NULL; - } - - return 1; - } - } - - r = socket_read_message(rtnl); - if (r < 0) - return r; - if (r > 0) - /* received message, so try to process straight away */ - 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(rtnl, true, left); - if (r < 0) - return r; - else if (r == 0) - return -ETIMEDOUT; - } -} - -int sd_netlink_get_events(sd_netlink *rtnl) { - assert_return(rtnl, -EINVAL); - assert_return(!rtnl_pid_changed(rtnl), -ECHILD); - - if (rtnl->rqueue_size == 0) - return POLLIN; - else - return 0; -} - -int sd_netlink_get_timeout(sd_netlink *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_netlink *rtnl = userdata; - int r; - - assert(rtnl); - - r = sd_netlink_process(rtnl, NULL); - if (r < 0) - return r; - - return 1; -} - -static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) { - sd_netlink *rtnl = userdata; - int r; - - assert(rtnl); - - r = sd_netlink_process(rtnl, NULL); - if (r < 0) - return r; - - return 1; -} - -static int prepare_callback(sd_event_source *s, void *userdata) { - sd_netlink *rtnl = userdata; - int r, e; - usec_t until; - - assert(s); - assert(rtnl); - - e = sd_netlink_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_netlink_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; -} - -int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int64_t 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->io_event_source, rtnl->fd, 0, io_callback, rtnl); - 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_description(rtnl->io_event_source, "rtnl-receive-message"); - 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_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(rtnl->time_event_source, priority); - if (r < 0) - goto fail; - - r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer"); - if (r < 0) - goto fail; - - return 0; - -fail: - sd_netlink_detach_event(rtnl); - return r; -} - -int sd_netlink_detach_event(sd_netlink *rtnl) { - assert_return(rtnl, -EINVAL); - assert_return(rtnl->event, -ENXIO); - - rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source); - - rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source); - - rtnl->event = sd_event_unref(rtnl->event); - - return 0; -} - -int sd_netlink_add_match(sd_netlink *rtnl, - uint16_t type, - sd_netlink_message_handler_t callback, - void *userdata) { - _cleanup_free_ struct match_callback *c = NULL; - int r; - - assert_return(rtnl, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(!rtnl_pid_changed(rtnl), -ECHILD); - - c = new0(struct match_callback, 1); - if (!c) - return -ENOMEM; - - c->callback = callback; - c->type = type; - c->userdata = userdata; - - switch (type) { - case RTM_NEWLINK: - case RTM_DELLINK: - r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK); - if (r < 0) - return r; - - break; - case RTM_NEWADDR: - case RTM_DELADDR: - r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR); - if (r < 0) - return r; - - r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_IFADDR); - if (r < 0) - return r; - - break; - case RTM_NEWROUTE: - case RTM_DELROUTE: - r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_ROUTE); - if (r < 0) - return r; - - r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_ROUTE); - if (r < 0) - return r; - break; - default: - return -EOPNOTSUPP; - } - - LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c); - - c = NULL; - - return 0; -} - -int sd_netlink_remove_match(sd_netlink *rtnl, - uint16_t type, - sd_netlink_message_handler_t callback, - void *userdata) { - struct match_callback *c; - int r; - - 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); - - switch (type) { - case RTM_NEWLINK: - case RTM_DELLINK: - r = socket_broadcast_group_unref(rtnl, RTNLGRP_LINK); - if (r < 0) - return r; - - break; - case RTM_NEWADDR: - case RTM_DELADDR: - r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_IFADDR); - if (r < 0) - return r; - - r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_IFADDR); - if (r < 0) - return r; - - break; - case RTM_NEWROUTE: - case RTM_DELROUTE: - r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_ROUTE); - if (r < 0) - return r; - - r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_ROUTE); - if (r < 0) - return r; - break; - default: - return -EOPNOTSUPP; - } - - return 1; - } - - return 0; -} diff --git a/src/libsystemd/sd-netlink/test-local-addresses.c b/src/libsystemd/sd-netlink/test-local-addresses.c deleted file mode 100644 index e0e28cc0cc..0000000000 --- a/src/libsystemd/sd-netlink/test-local-addresses.c +++ /dev/null @@ -1,56 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 . -***/ - -#include "af-list.h" -#include "alloc-util.h" -#include "in-addr-util.h" -#include "local-addresses.h" - -static void print_local_addresses(struct local_address *a, unsigned n) { - unsigned i; - - for (i = 0; i < n; i++) { - _cleanup_free_ char *b = NULL; - - assert_se(in_addr_to_string(a[i].family, &a[i].address, &b) >= 0); - printf("%s if%i scope=%i metric=%u address=%s\n", af_to_name(a[i].family), a[i].ifindex, a[i].scope, a[i].metric, b); - } -} - -int main(int argc, char *argv[]) { - struct local_address *a; - int n; - - a = NULL; - n = local_addresses(NULL, 0, AF_UNSPEC, &a); - assert_se(n >= 0); - - printf("Local Addresses:\n"); - print_local_addresses(a, (unsigned) n); - a = mfree(a); - - n = local_gateways(NULL, 0, AF_UNSPEC, &a); - assert_se(n >= 0); - - printf("Local Gateways:\n"); - print_local_addresses(a, (unsigned) n); - free(a); - - return 0; -} diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c deleted file mode 100644 index 58c2e892f5..0000000000 --- a/src/libsystemd/sd-netlink/test-netlink.c +++ /dev/null @@ -1,440 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Tom Gundersen - - 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 . -***/ - -#include -#include - -#include "sd-netlink.h" - -#include "ether-addr-util.h" -#include "macro.h" -#include "missing.h" -#include "netlink-util.h" -#include "socket-util.h" -#include "string-util.h" -#include "util.h" - -static void test_message_link_bridge(sd_netlink *rtnl) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; - uint32_t cost; - - assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_NEWLINK, 1) >= 0); - assert_se(sd_rtnl_message_link_set_family(message, PF_BRIDGE) >= 0); - assert_se(sd_netlink_message_open_container(message, IFLA_PROTINFO) >= 0); - assert_se(sd_netlink_message_append_u32(message, IFLA_BRPORT_COST, 10) >= 0); - assert_se(sd_netlink_message_close_container(message) >= 0); - - assert_se(sd_netlink_message_rewind(message) >= 0); - - assert_se(sd_netlink_message_enter_container(message, IFLA_PROTINFO) >= 0); - assert_se(sd_netlink_message_read_u32(message, IFLA_BRPORT_COST, &cost) >= 0); - assert_se(cost == 10); - assert_se(sd_netlink_message_exit_container(message) >= 0); -} - -static void test_link_configure(sd_netlink *rtnl, int ifindex) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; - const char *mac = "98:fe:94:3f:c6:18", *name = "test"; - char buffer[ETHER_ADDR_TO_STRING_MAX]; - unsigned int mtu = 1450, mtu_out; - const char *name_out; - struct ether_addr mac_out; - - /* we'd really like to test NEWLINK, but let's not mess with the running kernel */ - assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_GETLINK, ifindex) >= 0); - assert_se(sd_netlink_message_append_string(message, IFLA_IFNAME, name) >= 0); - assert_se(sd_netlink_message_append_ether_addr(message, IFLA_ADDRESS, ether_aton(mac)) >= 0); - assert_se(sd_netlink_message_append_u32(message, IFLA_MTU, mtu) >= 0); - - assert_se(sd_netlink_call(rtnl, message, 0, NULL) == 1); - assert_se(sd_netlink_message_rewind(message) >= 0); - - assert_se(sd_netlink_message_read_string(message, IFLA_IFNAME, &name_out) >= 0); - assert_se(streq(name, name_out)); - - assert_se(sd_netlink_message_read_ether_addr(message, IFLA_ADDRESS, &mac_out) >= 0); - assert_se(streq(mac, ether_addr_to_string(&mac_out, buffer))); - - assert_se(sd_netlink_message_read_u32(message, IFLA_MTU, &mtu_out) >= 0); - assert_se(mtu == mtu_out); -} - -static void test_link_get(sd_netlink *rtnl, int ifindex) { - sd_netlink_message *m; - sd_netlink_message *r; - unsigned int mtu = 1500; - const char *str_data; - uint8_t u8_data; - uint32_t u32_data; - struct ether_addr eth_data; - - assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); - assert_se(m); - - /* u8 test cases */ - assert_se(sd_netlink_message_append_u8(m, IFLA_CARRIER, 0) >= 0); - assert_se(sd_netlink_message_append_u8(m, IFLA_OPERSTATE, 0) >= 0); - assert_se(sd_netlink_message_append_u8(m, IFLA_LINKMODE, 0) >= 0); - - /* u32 test cases */ - assert_se(sd_netlink_message_append_u32(m, IFLA_MTU, mtu) >= 0); - assert_se(sd_netlink_message_append_u32(m, IFLA_GROUP, 0) >= 0); - assert_se(sd_netlink_message_append_u32(m, IFLA_TXQLEN, 0) >= 0); - assert_se(sd_netlink_message_append_u32(m, IFLA_NUM_TX_QUEUES, 0) >= 0); - assert_se(sd_netlink_message_append_u32(m, IFLA_NUM_RX_QUEUES, 0) >= 0); - - assert_se(sd_netlink_call(rtnl, m, -1, &r) == 1); - - assert_se(sd_netlink_message_read_string(r, IFLA_IFNAME, &str_data) == 0); - - assert_se(sd_netlink_message_read_u8(r, IFLA_CARRIER, &u8_data) == 0); - assert_se(sd_netlink_message_read_u8(r, IFLA_OPERSTATE, &u8_data) == 0); - assert_se(sd_netlink_message_read_u8(r, IFLA_LINKMODE, &u8_data) == 0); - - assert_se(sd_netlink_message_read_u32(r, IFLA_MTU, &u32_data) == 0); - assert_se(sd_netlink_message_read_u32(r, IFLA_GROUP, &u32_data) == 0); - assert_se(sd_netlink_message_read_u32(r, IFLA_TXQLEN, &u32_data) == 0); - assert_se(sd_netlink_message_read_u32(r, IFLA_NUM_TX_QUEUES, &u32_data) == 0); - assert_se(sd_netlink_message_read_u32(r, IFLA_NUM_RX_QUEUES, &u32_data) == 0); - - assert_se(sd_netlink_message_read_ether_addr(r, IFLA_ADDRESS, ð_data) == 0); - - assert_se((m = sd_netlink_message_unref(m)) == NULL); - assert_se((r = sd_netlink_message_unref(r)) == NULL); -} - - -static void test_address_get(sd_netlink *rtnl, int ifindex) { - sd_netlink_message *m; - sd_netlink_message *r; - struct in_addr in_data; - struct ifa_cacheinfo cache; - const char *label; - - assert_se(sd_rtnl_message_new_addr(rtnl, &m, RTM_GETADDR, ifindex, AF_INET) >= 0); - assert_se(m); - - assert_se(sd_netlink_call(rtnl, m, -1, &r) == 1); - - assert_se(sd_netlink_message_read_in_addr(r, IFA_LOCAL, &in_data) == 0); - assert_se(sd_netlink_message_read_in_addr(r, IFA_ADDRESS, &in_data) == 0); - assert_se(sd_netlink_message_read_string(r, IFA_LABEL, &label) == 0); - assert_se(sd_netlink_message_read_cache_info(r, IFA_CACHEINFO, &cache) == 0); - - assert_se((m = sd_netlink_message_unref(m)) == NULL); - assert_se((r = sd_netlink_message_unref(r)) == NULL); - -} - -static void test_route(void) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req; - struct in_addr addr, addr_data; - uint32_t index = 2, u32_data; - int r; - - r = sd_rtnl_message_new_route(NULL, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC); - if (r < 0) { - log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); - return; - } - - addr.s_addr = htonl(INADDR_LOOPBACK); - - r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &addr); - if (r < 0) { - log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); - return; - } - - r = sd_netlink_message_append_u32(req, RTA_OIF, index); - if (r < 0) { - log_error_errno(r, "Could not append RTA_OIF attribute: %m"); - return; - } - - assert_se(sd_netlink_message_rewind(req) >= 0); - - assert_se(sd_netlink_message_read_in_addr(req, RTA_GATEWAY, &addr_data) >= 0); - assert_se(addr_data.s_addr == addr.s_addr); - - assert_se(sd_netlink_message_read_u32(req, RTA_OIF, &u32_data) >= 0); - assert_se(u32_data == index); - - assert_se((req = sd_netlink_message_unref(req)) == NULL); -} - -static void test_multiple(void) { - sd_netlink *rtnl1, *rtnl2; - - assert_se(sd_netlink_open(&rtnl1) >= 0); - assert_se(sd_netlink_open(&rtnl2) >= 0); - - rtnl1 = sd_netlink_unref(rtnl1); - rtnl2 = sd_netlink_unref(rtnl2); -} - -static int link_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - char *ifname = userdata; - const char *data; - - assert_se(rtnl); - assert_se(m); - - log_info("got link info about %s", ifname); - free(ifname); - - assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &data) >= 0); - assert_se(streq(data, "lo")); - - return 1; -} - -static void test_event_loop(int ifindex) { - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - char *ifname; - - ifname = strdup("lo2"); - assert_se(ifname); - - assert_se(sd_netlink_open(&rtnl) >= 0); - assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); - - assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, NULL) >= 0); - - assert_se(sd_event_default(&event) >= 0); - - assert_se(sd_netlink_attach_event(rtnl, event, 0) >= 0); - - assert_se(sd_event_run(event, 0) >= 0); - - assert_se(sd_netlink_detach_event(rtnl) >= 0); - - assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); -} - -static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - int *counter = userdata; - int r; - - (*counter)--; - - r = sd_netlink_message_get_errno(m); - - log_info_errno(r, "%d left in pipe. got reply: %m", *counter); - - assert_se(r >= 0); - - return 1; -} - -static void test_async(int ifindex) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *r = NULL; - uint32_t serial; - char *ifname; - - ifname = strdup("lo"); - assert_se(ifname); - - assert_se(sd_netlink_open(&rtnl) >= 0); - - assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); - - assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, &serial) >= 0); - - assert_se(sd_netlink_wait(rtnl, 0) >= 0); - assert_se(sd_netlink_process(rtnl, &r) >= 0); - - assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); -} - -static void test_pipe(int ifindex) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m1 = NULL, *m2 = NULL; - int counter = 0; - - assert_se(sd_netlink_open(&rtnl) >= 0); - - assert_se(sd_rtnl_message_new_link(rtnl, &m1, RTM_GETLINK, ifindex) >= 0); - assert_se(sd_rtnl_message_new_link(rtnl, &m2, RTM_GETLINK, ifindex) >= 0); - - counter++; - assert_se(sd_netlink_call_async(rtnl, m1, pipe_handler, &counter, 0, NULL) >= 0); - - counter++; - assert_se(sd_netlink_call_async(rtnl, m2, pipe_handler, &counter, 0, NULL) >= 0); - - while (counter > 0) { - assert_se(sd_netlink_wait(rtnl, 0) >= 0); - assert_se(sd_netlink_process(rtnl, NULL) >= 0); - } - - assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); -} - -static void test_container(void) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - uint16_t u16_data; - uint32_t u32_data; - const char *string_data; - - assert_se(sd_rtnl_message_new_link(NULL, &m, RTM_NEWLINK, 0) >= 0); - - assert_se(sd_netlink_message_open_container(m, IFLA_LINKINFO) >= 0); - assert_se(sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "vlan") >= 0); - assert_se(sd_netlink_message_append_u16(m, IFLA_VLAN_ID, 100) >= 0); - assert_se(sd_netlink_message_close_container(m) >= 0); - assert_se(sd_netlink_message_append_string(m, IFLA_INFO_KIND, "vlan") >= 0); - assert_se(sd_netlink_message_close_container(m) >= 0); - assert_se(sd_netlink_message_close_container(m) == -EINVAL); - - assert_se(sd_netlink_message_rewind(m) >= 0); - - assert_se(sd_netlink_message_enter_container(m, IFLA_LINKINFO) >= 0); - assert_se(sd_netlink_message_read_string(m, IFLA_INFO_KIND, &string_data) >= 0); - assert_se(streq("vlan", string_data)); - - assert_se(sd_netlink_message_enter_container(m, IFLA_INFO_DATA) >= 0); - assert_se(sd_netlink_message_read_u16(m, IFLA_VLAN_ID, &u16_data) >= 0); - assert_se(sd_netlink_message_exit_container(m) >= 0); - - assert_se(sd_netlink_message_read_string(m, IFLA_INFO_KIND, &string_data) >= 0); - assert_se(streq("vlan", string_data)); - assert_se(sd_netlink_message_exit_container(m) >= 0); - - assert_se(sd_netlink_message_read_u32(m, IFLA_LINKINFO, &u32_data) < 0); - - assert_se(sd_netlink_message_exit_container(m) == -EINVAL); -} - -static void test_match(void) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - - assert_se(sd_netlink_open(&rtnl) >= 0); - - assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, link_handler, NULL) >= 0); - assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, link_handler, NULL) >= 0); - - assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 1); - assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 1); - assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 0); - - assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); -} - -static void test_get_addresses(sd_netlink *rtnl) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *m; - - assert_se(sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC) >= 0); - - assert_se(sd_netlink_call(rtnl, req, 0, &reply) >= 0); - - for (m = reply; m; m = sd_netlink_message_next(m)) { - uint16_t type; - unsigned char scope, flags; - int family, ifindex; - - assert_se(sd_netlink_message_get_type(m, &type) >= 0); - assert_se(type == RTM_NEWADDR); - - assert_se(sd_rtnl_message_addr_get_ifindex(m, &ifindex) >= 0); - assert_se(sd_rtnl_message_addr_get_family(m, &family) >= 0); - assert_se(sd_rtnl_message_addr_get_scope(m, &scope) >= 0); - assert_se(sd_rtnl_message_addr_get_flags(m, &flags) >= 0); - - assert_se(ifindex > 0); - assert_se(family == AF_INET || family == AF_INET6); - - log_info("got IPv%u address on ifindex %i", family == AF_INET ? 4: 6, ifindex); - } -} - -static void test_message(void) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - - assert_se(rtnl_message_new_synthetic_error(-ETIMEDOUT, 1, &m) >= 0); - assert_se(sd_netlink_message_get_errno(m) == -ETIMEDOUT); -} - -int main(void) { - sd_netlink *rtnl; - sd_netlink_message *m; - sd_netlink_message *r; - const char *string_data; - int if_loopback; - uint16_t type; - - test_message(); - - test_match(); - - test_multiple(); - - test_route(); - - test_container(); - - assert_se(sd_netlink_open(&rtnl) >= 0); - assert_se(rtnl); - - if_loopback = (int) if_nametoindex("lo"); - assert_se(if_loopback > 0); - - test_async(if_loopback); - - test_pipe(if_loopback); - - test_event_loop(if_loopback); - - test_link_configure(rtnl, if_loopback); - - test_get_addresses(rtnl); - - test_message_link_bridge(rtnl); - - assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, if_loopback) >= 0); - assert_se(m); - - assert_se(sd_netlink_message_get_type(m, &type) >= 0); - assert_se(type == RTM_GETLINK); - - assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &string_data) == -EPERM); - - assert_se(sd_netlink_call(rtnl, m, 0, &r) == 1); - assert_se(sd_netlink_message_get_type(r, &type) >= 0); - assert_se(type == RTM_NEWLINK); - - assert_se((r = sd_netlink_message_unref(r)) == NULL); - - assert_se(sd_netlink_call(rtnl, m, -1, &r) == -EPERM); - assert_se((m = sd_netlink_message_unref(m)) == NULL); - assert_se((r = sd_netlink_message_unref(r)) == NULL); - - test_link_get(rtnl, if_loopback); - test_address_get(rtnl, if_loopback); - - assert_se((m = sd_netlink_message_unref(m)) == NULL); - assert_se((r = sd_netlink_message_unref(r)) == NULL); - assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); - - return EXIT_SUCCESS; -} diff --git a/src/libsystemd/sd-network/Makefile b/src/libsystemd/sd-network/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd/sd-network/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-network/network-util.c b/src/libsystemd/sd-network/network-util.c deleted file mode 100644 index a0d9b5f1a4..0000000000 --- a/src/libsystemd/sd-network/network-util.c +++ /dev/null @@ -1,37 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 . -***/ - -#include "alloc-util.h" -#include "fd-util.h" -#include "network-util.h" -#include "strv.h" - -bool network_is_online(void) { - _cleanup_free_ char *state = NULL; - int r; - - r = sd_network_get_operational_state(&state); - if (r < 0) /* if we don't know anything, we consider the system online */ - return true; - - if (STR_IN_SET(state, "routable", "degraded")) - return true; - - return false; -} diff --git a/src/libsystemd/sd-network/network-util.h b/src/libsystemd/sd-network/network-util.h deleted file mode 100644 index 26780dce28..0000000000 --- a/src/libsystemd/sd-network/network-util.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Thomas Hindø Paabøl Andersen - - 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 . -***/ - -#include "sd-network.h" - -bool network_is_online(void); diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c deleted file mode 100644 index f8e18f23fd..0000000000 --- a/src/libsystemd/sd-network/sd-network.c +++ /dev/null @@ -1,400 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - Copyright 2014 Tom Gundersen - - 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 . -***/ - -#include -#include -#include -#include - -#include "sd-network.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "macro.h" -#include "parse-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -_public_ int sd_network_get_operational_state(char **state) { - _cleanup_free_ char *s = NULL; - int r; - - assert_return(state, -EINVAL); - - r = parse_env_file("/run/systemd/netif/state", NEWLINE, "OPER_STATE", &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - *state = s; - s = NULL; - - return 0; -} - -static int network_get_strv(const char *key, char ***ret) { - _cleanup_strv_free_ char **a = NULL; - _cleanup_free_ char *s = NULL; - int r; - - assert_return(ret, -EINVAL); - - r = parse_env_file("/run/systemd/netif/state", NEWLINE, key, &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) { - *ret = NULL; - return 0; - } - - a = strv_split(s, " "); - if (!a) - return -ENOMEM; - - strv_uniq(a); - r = strv_length(a); - - *ret = a; - a = NULL; - - return r; -} - -_public_ int sd_network_get_dns(char ***ret) { - return network_get_strv("DNS", ret); -} - -_public_ int sd_network_get_ntp(char ***ret) { - return network_get_strv("NTP", ret); -} - -_public_ int sd_network_get_search_domains(char ***ret) { - return network_get_strv("DOMAINS", ret); -} - -_public_ int sd_network_get_route_domains(char ***ret) { - return network_get_strv("ROUTE_DOMAINS", ret); -} - -static int network_link_get_string(int ifindex, const char *field, char **ret) { - char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; - _cleanup_free_ char *s = NULL; - int r; - - assert_return(ifindex > 0, -EINVAL); - assert_return(ret, -EINVAL); - - xsprintf(path, "/run/systemd/netif/links/%i", ifindex); - - r = parse_env_file(path, NEWLINE, field, &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - *ret = s; - s = NULL; - - return 0; -} - -static int network_link_get_strv(int ifindex, const char *key, char ***ret) { - char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; - _cleanup_strv_free_ char **a = NULL; - _cleanup_free_ char *s = NULL; - int r; - - assert_return(ifindex > 0, -EINVAL); - assert_return(ret, -EINVAL); - - xsprintf(path, "/run/systemd/netif/links/%i", ifindex); - r = parse_env_file(path, NEWLINE, key, &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) { - *ret = NULL; - return 0; - } - - a = strv_split(s, " "); - if (!a) - return -ENOMEM; - - strv_uniq(a); - r = strv_length(a); - - *ret = a; - a = NULL; - - return r; -} - -_public_ int sd_network_link_get_setup_state(int ifindex, char **state) { - return network_link_get_string(ifindex, "ADMIN_STATE", state); -} - -_public_ int sd_network_link_get_network_file(int ifindex, char **filename) { - return network_link_get_string(ifindex, "NETWORK_FILE", filename); -} - -_public_ int sd_network_link_get_operational_state(int ifindex, char **state) { - return network_link_get_string(ifindex, "OPER_STATE", state); -} - -_public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) { - return network_link_get_string(ifindex, "LLMNR", llmnr); -} - -_public_ int sd_network_link_get_mdns(int ifindex, char **mdns) { - return network_link_get_string(ifindex, "MDNS", mdns); -} - -_public_ int sd_network_link_get_dnssec(int ifindex, char **dnssec) { - return network_link_get_string(ifindex, "DNSSEC", dnssec); -} - -_public_ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta) { - return network_link_get_strv(ifindex, "DNSSEC_NTA", nta); -} - -_public_ int sd_network_link_get_timezone(int ifindex, char **ret) { - return network_link_get_string(ifindex, "TIMEZONE", ret); -} - -_public_ int sd_network_link_get_dns(int ifindex, char ***ret) { - return network_link_get_strv(ifindex, "DNS", ret); -} - -_public_ int sd_network_link_get_ntp(int ifindex, char ***ret) { - return network_link_get_strv(ifindex, "NTP", ret); -} - -_public_ int sd_network_link_get_search_domains(int ifindex, char ***ret) { - return network_link_get_strv(ifindex, "DOMAINS", ret); -} - -_public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) { - return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret); -} - -static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) { - char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; - _cleanup_free_ int *ifis = NULL; - _cleanup_free_ char *s = NULL; - size_t allocated = 0, c = 0; - const char *x; - int r; - - assert_return(ifindex > 0, -EINVAL); - assert_return(ret, -EINVAL); - - xsprintf(path, "/run/systemd/netif/links/%i", ifindex); - r = parse_env_file(path, NEWLINE, key, &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) { - *ret = NULL; - return 0; - } - - x = s; - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&x, &word, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - r = parse_ifindex(word, &ifindex); - if (r < 0) - return r; - - if (!GREEDY_REALLOC(ifis, allocated, c + 1)) - return -ENOMEM; - - ifis[c++] = ifindex; - } - - if (!GREEDY_REALLOC(ifis, allocated, c + 1)) - return -ENOMEM; - ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/ - - *ret = ifis; - ifis = NULL; - - return c; -} - -_public_ int sd_network_link_get_carrier_bound_to(int ifindex, int **ret) { - return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_TO", ret); -} - -_public_ int sd_network_link_get_carrier_bound_by(int ifindex, int **ret) { - return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_BY", ret); -} - -static inline int MONITOR_TO_FD(sd_network_monitor *m) { - return (int) (unsigned long) m - 1; -} - -static inline sd_network_monitor* FD_TO_MONITOR(int fd) { - return (sd_network_monitor*) (unsigned long) (fd + 1); -} - -static int monitor_add_inotify_watch(int fd) { - int k; - - k = inotify_add_watch(fd, "/run/systemd/netif/links/", IN_MOVED_TO|IN_DELETE); - if (k >= 0) - return 0; - else if (errno != ENOENT) - return -errno; - - k = inotify_add_watch(fd, "/run/systemd/netif/", IN_CREATE|IN_ISDIR); - if (k >= 0) - return 0; - else if (errno != ENOENT) - return -errno; - - k = inotify_add_watch(fd, "/run/systemd/", IN_CREATE|IN_ISDIR); - if (k < 0) - return -errno; - - return 0; -} - -_public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category) { - _cleanup_close_ int fd = -1; - int k; - bool good = false; - - assert_return(m, -EINVAL); - - fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (fd < 0) - return -errno; - - if (!category || streq(category, "links")) { - k = monitor_add_inotify_watch(fd); - if (k < 0) - return k; - - good = true; - } - - if (!good) - return -EINVAL; - - *m = FD_TO_MONITOR(fd); - fd = -1; - - return 0; -} - -_public_ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) { - int fd; - - if (m) { - fd = MONITOR_TO_FD(m); - close_nointr(fd); - } - - return NULL; -} - -_public_ int sd_network_monitor_flush(sd_network_monitor *m) { - union inotify_event_buffer buffer; - struct inotify_event *e; - ssize_t l; - int fd, k; - - assert_return(m, -EINVAL); - - fd = MONITOR_TO_FD(m); - - l = read(fd, &buffer, sizeof(buffer)); - if (l < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return -errno; - } - - FOREACH_INOTIFY_EVENT(e, buffer, l) { - if (e->mask & IN_ISDIR) { - k = monitor_add_inotify_watch(fd); - if (k < 0) - return k; - - k = inotify_rm_watch(fd, e->wd); - if (k < 0) - return -errno; - } - } - - return 0; -} - -_public_ int sd_network_monitor_get_fd(sd_network_monitor *m) { - - assert_return(m, -EINVAL); - - return MONITOR_TO_FD(m); -} - -_public_ int sd_network_monitor_get_events(sd_network_monitor *m) { - - assert_return(m, -EINVAL); - - /* For now we will only return POLLIN here, since we don't - * need anything else ever for inotify. However, let's have - * this API to keep our options open should we later on need - * it. */ - return POLLIN; -} - -_public_ int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec) { - - assert_return(m, -EINVAL); - assert_return(timeout_usec, -EINVAL); - - /* For now we will only return (uint64_t) -1, since we don't - * need any timeout. However, let's have this API to keep our - * options open should we later on need it. */ - *timeout_usec = (uint64_t) -1; - return 0; -} diff --git a/src/libsystemd/sd-path/Makefile b/src/libsystemd/sd-path/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd/sd-path/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c deleted file mode 100644 index b7aec1f20a..0000000000 --- a/src/libsystemd/sd-path/sd-path.c +++ /dev/null @@ -1,638 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 . -***/ - -#include "sd-path.h" - -#include "alloc-util.h" -#include "architecture.h" -#include "fd-util.h" -#include "fileio.h" -#include "missing.h" -#include "path-util.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" -#include "util.h" - -static int from_environment(const char *envname, const char *fallback, const char **ret) { - assert(ret); - - if (envname) { - const char *e; - - e = secure_getenv(envname); - if (e && path_is_absolute(e)) { - *ret = e; - return 0; - } - } - - if (fallback) { - *ret = fallback; - return 0; - } - - return -ENXIO; -} - -static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) { - _cleanup_free_ char *h = NULL; - char *cc = NULL; - int r; - - assert(suffix); - assert(buffer); - assert(ret); - - if (envname) { - const char *e = NULL; - - e = secure_getenv(envname); - if (e && path_is_absolute(e)) { - *ret = e; - return 0; - } - } - - r = get_home_dir(&h); - if (r < 0) - return r; - - if (endswith(h, "/")) - cc = strappend(h, suffix); - else - cc = strjoin(h, "/", suffix, NULL); - if (!cc) - return -ENOMEM; - - *buffer = cc; - *ret = cc; - return 0; -} - -static int from_user_dir(const char *field, char **buffer, const char **ret) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *b = NULL; - _cleanup_free_ const char *fn = NULL; - const char *c = NULL; - char line[LINE_MAX]; - size_t n; - int r; - - assert(field); - assert(buffer); - assert(ret); - - r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c); - if (r < 0) - return r; - - fn = strappend(c, "/user-dirs.dirs"); - if (!fn) - return -ENOMEM; - - f = fopen(fn, "re"); - if (!f) { - if (errno == ENOENT) - goto fallback; - - return -errno; - } - - /* This is an awful parse, but it follows closely what - * xdg-user-dirs does upstream */ - - n = strlen(field); - FOREACH_LINE(line, f, return -errno) { - char *l, *p, *e; - - l = strstrip(line); - - if (!strneq(l, field, n)) - continue; - - p = l + n; - p += strspn(p, WHITESPACE); - - if (*p != '=') - continue; - p++; - - p += strspn(p, WHITESPACE); - - if (*p != '"') - continue; - p++; - - e = strrchr(p, '"'); - if (!e) - continue; - *e = 0; - - /* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */ - if (startswith(p, "$HOME/")) { - _cleanup_free_ char *h = NULL; - char *cc; - - r = get_home_dir(&h); - if (r < 0) - return r; - - cc = strappend(h, p+5); - if (!cc) - return -ENOMEM; - - *buffer = cc; - *ret = cc; - return 0; - } else if (streq(p, "$HOME")) { - - r = get_home_dir(buffer); - if (r < 0) - return r; - - *ret = *buffer; - return 0; - } else if (path_is_absolute(p)) { - char *copy; - - copy = strdup(p); - if (!copy) - return -ENOMEM; - - *buffer = copy; - *ret = copy; - return 0; - } - } - -fallback: - /* The desktop directory defaults to $HOME/Desktop, the others to $HOME */ - if (streq(field, "XDG_DESKTOP_DIR")) { - _cleanup_free_ char *h = NULL; - char *cc; - - r = get_home_dir(&h); - if (r < 0) - return r; - - cc = strappend(h, "/Desktop"); - if (!cc) - return -ENOMEM; - - *buffer = cc; - *ret = cc; - } else { - - r = get_home_dir(buffer); - if (r < 0) - return r; - - *ret = *buffer; - } - - return 0; -} - -static int get_path(uint64_t type, char **buffer, const char **ret) { - int r; - - assert(buffer); - assert(ret); - - switch (type) { - - case SD_PATH_TEMPORARY: - return from_environment("TMPDIR", "/tmp", ret); - - case SD_PATH_TEMPORARY_LARGE: - return from_environment("TMPDIR", "/var/tmp", ret); - - case SD_PATH_SYSTEM_BINARIES: - *ret = "/usr/bin"; - return 0; - - case SD_PATH_SYSTEM_INCLUDE: - *ret = "/usr/include"; - return 0; - - case SD_PATH_SYSTEM_LIBRARY_PRIVATE: - *ret = "/usr/lib"; - return 0; - - case SD_PATH_SYSTEM_LIBRARY_ARCH: - *ret = LIBDIR; - return 0; - - case SD_PATH_SYSTEM_SHARED: - *ret = "/usr/share"; - return 0; - - case SD_PATH_SYSTEM_CONFIGURATION_FACTORY: - *ret = "/usr/share/factory/etc"; - return 0; - - case SD_PATH_SYSTEM_STATE_FACTORY: - *ret = "/usr/share/factory/var"; - return 0; - - case SD_PATH_SYSTEM_CONFIGURATION: - *ret = "/etc"; - return 0; - - case SD_PATH_SYSTEM_RUNTIME: - *ret = "/run"; - return 0; - - case SD_PATH_SYSTEM_RUNTIME_LOGS: - *ret = "/run/log"; - return 0; - - case SD_PATH_SYSTEM_STATE_PRIVATE: - *ret = "/var/lib"; - return 0; - - case SD_PATH_SYSTEM_STATE_LOGS: - *ret = "/var/log"; - return 0; - - case SD_PATH_SYSTEM_STATE_CACHE: - *ret = "/var/cache"; - return 0; - - case SD_PATH_SYSTEM_STATE_SPOOL: - *ret = "/var/spool"; - return 0; - - case SD_PATH_USER_BINARIES: - return from_home_dir(NULL, ".local/bin", buffer, ret); - - case SD_PATH_USER_LIBRARY_PRIVATE: - return from_home_dir(NULL, ".local/lib", buffer, ret); - - case SD_PATH_USER_LIBRARY_ARCH: - return from_home_dir(NULL, ".local/lib/" LIB_ARCH_TUPLE, buffer, ret); - - case SD_PATH_USER_SHARED: - return from_home_dir("XDG_DATA_HOME", ".local/share", buffer, ret); - - case SD_PATH_USER_CONFIGURATION: - return from_home_dir("XDG_CONFIG_HOME", ".config", buffer, ret); - - case SD_PATH_USER_RUNTIME: - return from_environment("XDG_RUNTIME_DIR", NULL, ret); - - case SD_PATH_USER_STATE_CACHE: - return from_home_dir("XDG_CACHE_HOME", ".cache", buffer, ret); - - case SD_PATH_USER: - r = get_home_dir(buffer); - if (r < 0) - return r; - - *ret = *buffer; - return 0; - - case SD_PATH_USER_DOCUMENTS: - return from_user_dir("XDG_DOCUMENTS_DIR", buffer, ret); - - case SD_PATH_USER_MUSIC: - return from_user_dir("XDG_MUSIC_DIR", buffer, ret); - - case SD_PATH_USER_PICTURES: - return from_user_dir("XDG_PICTURES_DIR", buffer, ret); - - case SD_PATH_USER_VIDEOS: - return from_user_dir("XDG_VIDEOS_DIR", buffer, ret); - - case SD_PATH_USER_DOWNLOAD: - return from_user_dir("XDG_DOWNLOAD_DIR", buffer, ret); - - case SD_PATH_USER_PUBLIC: - return from_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret); - - case SD_PATH_USER_TEMPLATES: - return from_user_dir("XDG_TEMPLATES_DIR", buffer, ret); - - case SD_PATH_USER_DESKTOP: - return from_user_dir("XDG_DESKTOP_DIR", buffer, ret); - } - - return -EOPNOTSUPP; -} - -_public_ int sd_path_home(uint64_t type, const char *suffix, char **path) { - char *buffer = NULL, *cc; - const char *ret; - int r; - - assert_return(path, -EINVAL); - - if (IN_SET(type, - SD_PATH_SEARCH_BINARIES, - SD_PATH_SEARCH_LIBRARY_PRIVATE, - SD_PATH_SEARCH_LIBRARY_ARCH, - SD_PATH_SEARCH_SHARED, - SD_PATH_SEARCH_CONFIGURATION_FACTORY, - SD_PATH_SEARCH_STATE_FACTORY, - SD_PATH_SEARCH_CONFIGURATION)) { - - _cleanup_strv_free_ char **l = NULL; - - r = sd_path_search(type, suffix, &l); - if (r < 0) - return r; - - buffer = strv_join(l, ":"); - if (!buffer) - return -ENOMEM; - - *path = buffer; - return 0; - } - - r = get_path(type, &buffer, &ret); - if (r < 0) - return r; - - if (!suffix) { - if (!buffer) { - buffer = strdup(ret); - if (!buffer) - return -ENOMEM; - } - - *path = buffer; - return 0; - } - - suffix += strspn(suffix, "/"); - - if (endswith(ret, "/")) - cc = strappend(ret, suffix); - else - cc = strjoin(ret, "/", suffix, NULL); - - free(buffer); - - if (!cc) - return -ENOMEM; - - *path = cc; - return 0; -} - -static int search_from_environment( - char ***list, - const char *env_home, - const char *home_suffix, - const char *env_search, - bool env_search_sufficient, - const char *first, ...) { - - const char *e; - char *h = NULL; - char **l = NULL; - int r; - - assert(list); - - if (env_search) { - e = secure_getenv(env_search); - if (e) { - l = strv_split(e, ":"); - if (!l) - return -ENOMEM; - - if (env_search_sufficient) { - *list = l; - return 0; - } - } - } - - if (!l && first) { - va_list ap; - - va_start(ap, first); - l = strv_new_ap(first, ap); - va_end(ap); - - if (!l) - return -ENOMEM; - } - - if (env_home) { - e = secure_getenv(env_home); - if (e && path_is_absolute(e)) { - h = strdup(e); - if (!h) { - strv_free(l); - return -ENOMEM; - } - } - } - - if (!h && home_suffix) { - e = secure_getenv("HOME"); - if (e && path_is_absolute(e)) { - if (endswith(e, "/")) - h = strappend(e, home_suffix); - else - h = strjoin(e, "/", home_suffix, NULL); - - if (!h) { - strv_free(l); - return -ENOMEM; - } - } - } - - if (h) { - r = strv_consume_prepend(&l, h); - if (r < 0) { - strv_free(l); - return -ENOMEM; - } - } - - *list = l; - return 0; -} - -static int get_search(uint64_t type, char ***list) { - - assert(list); - - switch(type) { - - case SD_PATH_SEARCH_BINARIES: - return search_from_environment(list, - NULL, - ".local/bin", - "PATH", - true, - "/usr/local/sbin", - "/usr/local/bin", - "/usr/sbin", - "/usr/bin", -#ifdef HAVE_SPLIT_USR - "/sbin", - "/bin", -#endif - NULL); - - case SD_PATH_SEARCH_LIBRARY_PRIVATE: - return search_from_environment(list, - NULL, - ".local/lib", - NULL, - false, - "/usr/local/lib", - "/usr/lib", -#ifdef HAVE_SPLIT_USR - "/lib", -#endif - NULL); - - case SD_PATH_SEARCH_LIBRARY_ARCH: - return search_from_environment(list, - NULL, - ".local/lib/" LIB_ARCH_TUPLE, - "LD_LIBRARY_PATH", - true, - LIBDIR, -#ifdef HAVE_SPLIT_USR - ROOTLIBDIR, -#endif - NULL); - - case SD_PATH_SEARCH_SHARED: - return search_from_environment(list, - "XDG_DATA_HOME", - ".local/share", - "XDG_DATA_DIRS", - false, - "/usr/local/share", - "/usr/share", - NULL); - - case SD_PATH_SEARCH_CONFIGURATION_FACTORY: - return search_from_environment(list, - NULL, - NULL, - NULL, - false, - "/usr/local/share/factory/etc", - "/usr/share/factory/etc", - NULL); - - case SD_PATH_SEARCH_STATE_FACTORY: - return search_from_environment(list, - NULL, - NULL, - NULL, - false, - "/usr/local/share/factory/var", - "/usr/share/factory/var", - NULL); - - case SD_PATH_SEARCH_CONFIGURATION: - return search_from_environment(list, - "XDG_CONFIG_HOME", - ".config", - "XDG_CONFIG_DIRS", - false, - "/etc", - NULL); - } - - return -EOPNOTSUPP; -} - -_public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) { - char **l, **i, **j, **n; - int r; - - assert_return(paths, -EINVAL); - - if (!IN_SET(type, - SD_PATH_SEARCH_BINARIES, - SD_PATH_SEARCH_LIBRARY_PRIVATE, - SD_PATH_SEARCH_LIBRARY_ARCH, - SD_PATH_SEARCH_SHARED, - SD_PATH_SEARCH_CONFIGURATION_FACTORY, - SD_PATH_SEARCH_STATE_FACTORY, - SD_PATH_SEARCH_CONFIGURATION)) { - - char *p; - - r = sd_path_home(type, suffix, &p); - if (r < 0) - return r; - - l = new(char*, 2); - if (!l) { - free(p); - return -ENOMEM; - } - - l[0] = p; - l[1] = NULL; - - *paths = l; - return 0; - } - - r = get_search(type, &l); - if (r < 0) - return r; - - if (!suffix) { - *paths = l; - return 0; - } - - n = new(char*, strv_length(l)+1); - if (!n) { - strv_free(l); - return -ENOMEM; - } - - j = n; - STRV_FOREACH(i, l) { - - if (endswith(*i, "/")) - *j = strappend(*i, suffix); - else - *j = strjoin(*i, "/", suffix, NULL); - - if (!*j) { - strv_free(l); - strv_free(n); - return -ENOMEM; - } - - j++; - } - - *j = NULL; - *paths = n; - return 0; -} diff --git a/src/libsystemd/sd-resolve/Makefile b/src/libsystemd/sd-resolve/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-resolve/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c deleted file mode 100644 index d8303e2e69..0000000000 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ /dev/null @@ -1,1245 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-resolve.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "io-util.h" -#include "list.h" -#include "missing.h" -#include "socket-util.h" -#include "util.h" - -#define WORKERS_MIN 1U -#define WORKERS_MAX 16U -#define QUERIES_MAX 256U -#define BUFSIZE 10240U - -typedef enum { - REQUEST_ADDRINFO, - RESPONSE_ADDRINFO, - REQUEST_NAMEINFO, - RESPONSE_NAMEINFO, - REQUEST_TERMINATE, - RESPONSE_DIED -} QueryType; - -enum { - REQUEST_RECV_FD, - REQUEST_SEND_FD, - RESPONSE_RECV_FD, - RESPONSE_SEND_FD, - _FD_MAX -}; - -struct sd_resolve { - unsigned n_ref; - - bool dead:1; - pid_t original_pid; - - int fds[_FD_MAX]; - - pthread_t workers[WORKERS_MAX]; - unsigned n_valid_workers; - - unsigned current_id; - sd_resolve_query* query_array[QUERIES_MAX]; - unsigned n_queries, n_done, n_outstanding; - - sd_event_source *event_source; - sd_event *event; - - sd_resolve_query *current; - - sd_resolve **default_resolve_ptr; - pid_t tid; - - LIST_HEAD(sd_resolve_query, queries); -}; - -struct sd_resolve_query { - unsigned n_ref; - - sd_resolve *resolve; - - QueryType type:4; - bool done:1; - bool floating:1; - unsigned id; - - int ret; - int _errno; - int _h_errno; - struct addrinfo *addrinfo; - char *serv, *host; - - union { - sd_resolve_getaddrinfo_handler_t getaddrinfo_handler; - sd_resolve_getnameinfo_handler_t getnameinfo_handler; - }; - - void *userdata; - - LIST_FIELDS(sd_resolve_query, queries); -}; - -typedef struct RHeader { - QueryType type; - unsigned id; - size_t length; -} RHeader; - -typedef struct AddrInfoRequest { - struct RHeader header; - bool hints_valid; - 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; - bool gethost:1, getserv:1; -} NameInfoRequest; - -typedef struct NameInfoResponse { - struct RHeader header; - size_t hostlen, servlen; - int ret; - int _errno; - int _h_errno; -} NameInfoResponse; - -typedef union Packet { - RHeader rheader; - AddrInfoRequest addrinfo_request; - AddrInfoResponse addrinfo_response; - NameInfoRequest nameinfo_request; - NameInfoResponse nameinfo_response; -} Packet; - -static int getaddrinfo_done(sd_resolve_query* q); -static int getnameinfo_done(sd_resolve_query *q); - -static void resolve_query_disconnect(sd_resolve_query *q); - -#define RESOLVE_DONT_DESTROY(resolve) \ - _cleanup_(sd_resolve_unrefp) _unused_ sd_resolve *_dont_destroy_##resolve = sd_resolve_ref(resolve) - -static int send_died(int out_fd) { - - RHeader rh = { - .type = RESPONSE_DIED, - .length = sizeof(RHeader), - }; - - assert(out_fd >= 0); - - if (send(out_fd, &rh, rh.length, MSG_NOSIGNAL) < 0) - return -errno; - - return 0; -} - -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); - memcpy_safe((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, - ai->ai_canonname, cnl); - - *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 resp = { - .header.type = RESPONSE_ADDRINFO, - .header.id = id, - .header.length = sizeof(AddrInfoResponse), - .ret = ret, - ._errno = _errno, - ._h_errno = _h_errno, - }; - - struct msghdr mh = {}; - struct iovec iov[2]; - union { - AddrInfoSerialization ais; - uint8_t space[BUFSIZE]; - } buffer; - - assert(out_fd >= 0); - - if (ret == 0 && ai) { - void *p = &buffer; - struct addrinfo *k; - - for (k = ai; k; k = k->ai_next) { - p = serialize_addrinfo(p, k, &resp.header.length, (uint8_t*) &buffer + BUFSIZE - (uint8_t*) p); - if (!p) { - freeaddrinfo(ai); - return -ENOBUFS; - } - } - } - - if (ai) - freeaddrinfo(ai); - - iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(AddrInfoResponse) }; - iov[1] = (struct iovec) { .iov_base = &buffer, .iov_len = resp.header.length - sizeof(AddrInfoResponse) }; - - mh.msg_iov = iov; - mh.msg_iovlen = ELEMENTSOF(iov); - - if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) - return -errno; - - return 0; -} - -static int send_nameinfo_reply( - int out_fd, - unsigned id, - int ret, - const char *host, - const char *serv, - int _errno, - int _h_errno) { - - NameInfoResponse resp = { - .header.type = RESPONSE_NAMEINFO, - .header.id = id, - .ret = ret, - ._errno = _errno, - ._h_errno = _h_errno, - }; - - struct msghdr mh = {}; - struct iovec iov[3]; - size_t hl, sl; - - assert(out_fd >= 0); - - sl = serv ? strlen(serv)+1 : 0; - hl = host ? strlen(host)+1 : 0; - - resp.header.length = sizeof(NameInfoResponse) + hl + sl; - resp.hostlen = hl; - resp.servlen = sl; - - iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(NameInfoResponse) }; - iov[1] = (struct iovec) { .iov_base = (void*) host, .iov_len = hl }; - iov[2] = (struct iovec) { .iov_base = (void*) serv, .iov_len = sl }; - - mh.msg_iov = iov; - mh.msg_iovlen = ELEMENTSOF(iov); - - if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) - return -errno; - - return 0; -} - -static int handle_request(int out_fd, const Packet *packet, size_t length) { - const RHeader *req; - - assert(out_fd >= 0); - assert(packet); - - req = &packet->rheader; - - assert(length >= sizeof(RHeader)); - assert(length == req->length); - - switch (req->type) { - - case REQUEST_ADDRINFO: { - const AddrInfoRequest *ai_req = &packet->addrinfo_request; - struct addrinfo hints = {}, *result = NULL; - const char *node, *service; - int ret; - - assert(length >= sizeof(AddrInfoRequest)); - assert(length == sizeof(AddrInfoRequest) + ai_req->node_len + ai_req->service_len); - - hints.ai_flags = ai_req->ai_flags; - hints.ai_family = ai_req->ai_family; - hints.ai_socktype = ai_req->ai_socktype; - hints.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_valid ? &hints : NULL, - &result); - - /* send_addrinfo_reply() frees result */ - return send_addrinfo_reply(out_fd, req->id, ret, result, errno, h_errno); - } - - case REQUEST_NAMEINFO: { - const NameInfoRequest *ni_req = &packet->nameinfo_request; - char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV]; - union sockaddr_union sa; - int ret; - - assert(length >= sizeof(NameInfoRequest)); - assert(length == sizeof(NameInfoRequest) + ni_req->sockaddr_len); - assert(sizeof(sa) >= ni_req->sockaddr_len); - - memcpy(&sa, (const uint8_t *) ni_req + sizeof(NameInfoRequest), ni_req->sockaddr_len); - - ret = getnameinfo(&sa.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_TERMINATE: - /* Quit */ - return -ECONNRESET; - - default: - assert_not_reached("Unknown request"); - } - - return 0; -} - -static void* thread_worker(void *p) { - sd_resolve *resolve = p; - sigset_t fullset; - - /* No signals in this thread please */ - assert_se(sigfillset(&fullset) == 0); - assert_se(pthread_sigmask(SIG_BLOCK, &fullset, NULL) == 0); - - /* Assign a pretty name to this thread */ - (void) prctl(PR_SET_NAME, (unsigned long) "sd-resolve"); - - while (!resolve->dead) { - union { - Packet packet; - uint8_t space[BUFSIZE]; - } buf; - ssize_t length; - - length = recv(resolve->fds[REQUEST_RECV_FD], &buf, sizeof(buf), 0); - if (length < 0) { - if (errno == EINTR) - continue; - - break; - } - if (length == 0) - break; - - if (resolve->dead) - break; - - if (handle_request(resolve->fds[RESPONSE_SEND_FD], &buf.packet, (size_t) length) < 0) - break; - } - - send_died(resolve->fds[RESPONSE_SEND_FD]); - - return NULL; -} - -static int start_threads(sd_resolve *resolve, unsigned extra) { - unsigned n; - int r; - - n = resolve->n_outstanding + extra; - n = CLAMP(n, WORKERS_MIN, WORKERS_MAX); - - while (resolve->n_valid_workers < n) { - - r = pthread_create(&resolve->workers[resolve->n_valid_workers], NULL, thread_worker, resolve); - if (r != 0) - return -r; - - resolve->n_valid_workers++; - } - - return 0; -} - -static bool resolve_pid_changed(sd_resolve *r) { - assert(r); - - /* We don't support people creating a resolver and keeping it - * around after fork(). Let's complain. */ - - return r->original_pid != getpid(); -} - -_public_ int sd_resolve_new(sd_resolve **ret) { - sd_resolve *resolve = NULL; - int i, r; - - assert_return(ret, -EINVAL); - - resolve = new0(sd_resolve, 1); - if (!resolve) - return -ENOMEM; - - resolve->n_ref = 1; - resolve->original_pid = getpid(); - - for (i = 0; i < _FD_MAX; i++) - resolve->fds[i] = -1; - - r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + REQUEST_RECV_FD); - if (r < 0) { - r = -errno; - goto fail; - } - - r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + RESPONSE_RECV_FD); - if (r < 0) { - r = -errno; - goto fail; - } - - fd_inc_sndbuf(resolve->fds[REQUEST_SEND_FD], QUERIES_MAX * BUFSIZE); - fd_inc_rcvbuf(resolve->fds[REQUEST_RECV_FD], QUERIES_MAX * BUFSIZE); - fd_inc_sndbuf(resolve->fds[RESPONSE_SEND_FD], QUERIES_MAX * BUFSIZE); - fd_inc_rcvbuf(resolve->fds[RESPONSE_RECV_FD], QUERIES_MAX * BUFSIZE); - - fd_nonblock(resolve->fds[RESPONSE_RECV_FD], true); - - *ret = resolve; - return 0; - -fail: - sd_resolve_unref(resolve); - return r; -} - -_public_ int sd_resolve_default(sd_resolve **ret) { - - static thread_local sd_resolve *default_resolve = NULL; - sd_resolve *e = NULL; - int r; - - if (!ret) - return !!default_resolve; - - if (default_resolve) { - *ret = sd_resolve_ref(default_resolve); - return 0; - } - - r = sd_resolve_new(&e); - if (r < 0) - return r; - - e->default_resolve_ptr = &default_resolve; - e->tid = gettid(); - default_resolve = e; - - *ret = e; - return 1; -} - -_public_ int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid) { - assert_return(resolve, -EINVAL); - assert_return(tid, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - if (resolve->tid != 0) { - *tid = resolve->tid; - return 0; - } - - if (resolve->event) - return sd_event_get_tid(resolve->event, tid); - - return -ENXIO; -} - -static void resolve_free(sd_resolve *resolve) { - PROTECT_ERRNO; - sd_resolve_query *q; - unsigned i; - - assert(resolve); - - while ((q = resolve->queries)) { - assert(q->floating); - resolve_query_disconnect(q); - sd_resolve_query_unref(q); - } - - if (resolve->default_resolve_ptr) - *(resolve->default_resolve_ptr) = NULL; - - resolve->dead = true; - - sd_resolve_detach_event(resolve); - - if (resolve->fds[REQUEST_SEND_FD] >= 0) { - - RHeader req = { - .type = REQUEST_TERMINATE, - .length = sizeof(req) - }; - - /* Send one termination packet for each worker */ - for (i = 0; i < resolve->n_valid_workers; i++) - (void) send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL); - } - - /* Now terminate them and wait until they are gone. - If we get an error than most likely the thread already exited. */ - for (i = 0; i < resolve->n_valid_workers; i++) - (void) pthread_join(resolve->workers[i], NULL); - - /* Close all communication channels */ - for (i = 0; i < _FD_MAX; i++) - safe_close(resolve->fds[i]); - - free(resolve); -} - -_public_ sd_resolve* sd_resolve_ref(sd_resolve *resolve) { - assert_return(resolve, NULL); - - assert(resolve->n_ref >= 1); - resolve->n_ref++; - - return resolve; -} - -_public_ sd_resolve* sd_resolve_unref(sd_resolve *resolve) { - - if (!resolve) - return NULL; - - assert(resolve->n_ref >= 1); - resolve->n_ref--; - - if (resolve->n_ref <= 0) - resolve_free(resolve); - - return NULL; -} - -_public_ int sd_resolve_get_fd(sd_resolve *resolve) { - assert_return(resolve, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - return resolve->fds[RESPONSE_RECV_FD]; -} - -_public_ int sd_resolve_get_events(sd_resolve *resolve) { - assert_return(resolve, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - return resolve->n_queries > resolve->n_done ? POLLIN : 0; -} - -_public_ int sd_resolve_get_timeout(sd_resolve *resolve, uint64_t *usec) { - assert_return(resolve, -EINVAL); - assert_return(usec, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - *usec = (uint64_t) -1; - return 0; -} - -static sd_resolve_query *lookup_query(sd_resolve *resolve, unsigned id) { - sd_resolve_query *q; - - assert(resolve); - - q = resolve->query_array[id % QUERIES_MAX]; - if (q) - if (q->id == id) - return q; - - return NULL; -} - -static int complete_query(sd_resolve *resolve, sd_resolve_query *q) { - int r; - - assert(q); - assert(!q->done); - assert(q->resolve == resolve); - - q->done = true; - resolve->n_done++; - - resolve->current = sd_resolve_query_ref(q); - - switch (q->type) { - - case REQUEST_ADDRINFO: - r = getaddrinfo_done(q); - break; - - case REQUEST_NAMEINFO: - r = getnameinfo_done(q); - break; - - default: - assert_not_reached("Cannot complete unknown query type"); - } - - resolve->current = NULL; - - if (q->floating) { - resolve_query_disconnect(q); - sd_resolve_query_unref(q); - } - - sd_resolve_query_unref(q); - - return r; -} - -static int unserialize_addrinfo(const void **p, size_t *length, struct addrinfo **ret_ai) { - AddrInfoSerialization s; - size_t l; - struct addrinfo *ai; - - assert(p); - assert(*p); - assert(ret_ai); - assert(length); - - if (*length < sizeof(AddrInfoSerialization)) - return -EBADMSG; - - memcpy(&s, *p, sizeof(s)); - - l = sizeof(AddrInfoSerialization) + s.ai_addrlen + s.canonname_len; - if (*length < l) - return -EBADMSG; - - ai = new0(struct addrinfo, 1); - if (!ai) - return -ENOMEM; - - 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 (s.ai_addrlen > 0) { - ai->ai_addr = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization), s.ai_addrlen); - if (!ai->ai_addr) { - free(ai); - return -ENOMEM; - } - } - - if (s.canonname_len > 0) { - ai->ai_canonname = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization) + s.ai_addrlen, s.canonname_len); - if (!ai->ai_canonname) { - free(ai->ai_addr); - free(ai); - return -ENOMEM; - } - } - - *length -= l; - *ret_ai = ai; - *p = ((const uint8_t*) *p) + l; - - return 0; -} - -static int handle_response(sd_resolve *resolve, const Packet *packet, size_t length) { - const RHeader *resp; - sd_resolve_query *q; - int r; - - assert(resolve); - - resp = &packet->rheader; - assert(resp); - assert(length >= sizeof(RHeader)); - assert(length == resp->length); - - if (resp->type == RESPONSE_DIED) { - resolve->dead = true; - return 0; - } - - assert(resolve->n_outstanding > 0); - resolve->n_outstanding--; - - 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; - - r = unserialize_addrinfo(&p, &l, &ai); - if (r < 0) { - q->ret = EAI_SYSTEM; - q->_errno = -r; - q->_h_errno = 0; - freeaddrinfo(q->addrinfo); - q->addrinfo = NULL; - break; - } - - if (prev) - prev->ai_next = ai; - else - q->addrinfo = ai; - - prev = ai; - } - - return complete_query(resolve, q); - } - - 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 > 0) { - q->host = strndup((const char*) ni_resp + sizeof(NameInfoResponse), ni_resp->hostlen-1); - if (!q->host) { - q->ret = EAI_MEMORY; - q->_errno = ENOMEM; - q->_h_errno = 0; - } - } - - if (ni_resp->servlen > 0) { - q->serv = strndup((const char*) ni_resp + sizeof(NameInfoResponse) + ni_resp->hostlen, ni_resp->servlen-1); - if (!q->serv) { - q->ret = EAI_MEMORY; - q->_errno = ENOMEM; - q->_h_errno = 0; - } - } - - return complete_query(resolve, q); - } - - default: - return 0; - } -} - -_public_ int sd_resolve_process(sd_resolve *resolve) { - RESOLVE_DONT_DESTROY(resolve); - - union { - Packet packet; - uint8_t space[BUFSIZE]; - } buf; - ssize_t l; - int r; - - assert_return(resolve, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - /* We don't allow recursively invoking sd_resolve_process(). */ - assert_return(!resolve->current, -EBUSY); - - l = recv(resolve->fds[RESPONSE_RECV_FD], &buf, sizeof(buf), 0); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - return -errno; - } - if (l == 0) - return -ECONNREFUSED; - - r = handle_response(resolve, &buf.packet, (size_t) l); - if (r < 0) - return r; - - return 1; -} - -_public_ int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec) { - int r; - - assert_return(resolve, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - if (resolve->n_done >= resolve->n_queries) - return 0; - - do { - r = fd_wait_for_event(resolve->fds[RESPONSE_RECV_FD], POLLIN, timeout_usec); - } while (r == -EINTR); - - if (r < 0) - return r; - - return sd_resolve_process(resolve); -} - -static int alloc_query(sd_resolve *resolve, bool floating, sd_resolve_query **_q) { - sd_resolve_query *q; - int r; - - assert(resolve); - assert(_q); - - if (resolve->n_queries >= QUERIES_MAX) - return -ENOBUFS; - - r = start_threads(resolve, 1); - if (r < 0) - return r; - - while (resolve->query_array[resolve->current_id % QUERIES_MAX]) - resolve->current_id++; - - q = resolve->query_array[resolve->current_id % QUERIES_MAX] = new0(sd_resolve_query, 1); - if (!q) - return -ENOMEM; - - q->n_ref = 1; - q->resolve = resolve; - q->floating = floating; - q->id = resolve->current_id++; - - if (!floating) - sd_resolve_ref(resolve); - - LIST_PREPEND(queries, resolve->queries, q); - resolve->n_queries++; - - *_q = q; - return 0; -} - -_public_ int sd_resolve_getaddrinfo( - sd_resolve *resolve, - sd_resolve_query **_q, - const char *node, const char *service, - const struct addrinfo *hints, - sd_resolve_getaddrinfo_handler_t callback, void *userdata) { - - AddrInfoRequest req = {}; - struct msghdr mh = {}; - struct iovec iov[3]; - sd_resolve_query *q; - int r; - - assert_return(resolve, -EINVAL); - assert_return(node || service, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - r = alloc_query(resolve, !_q, &q); - if (r < 0) - return r; - - q->type = REQUEST_ADDRINFO; - q->getaddrinfo_handler = callback; - q->userdata = userdata; - - req.node_len = node ? strlen(node)+1 : 0; - req.service_len = service ? strlen(service)+1 : 0; - - req.header.id = q->id; - req.header.type = REQUEST_ADDRINFO; - req.header.length = sizeof(AddrInfoRequest) + req.node_len + req.service_len; - - if (hints) { - req.hints_valid = true; - req.ai_flags = hints->ai_flags; - req.ai_family = hints->ai_family; - req.ai_socktype = hints->ai_socktype; - req.ai_protocol = hints->ai_protocol; - } - - iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(AddrInfoRequest) }; - if (node) - iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) node, .iov_len = req.node_len }; - if (service) - iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) service, .iov_len = req.service_len }; - mh.msg_iov = iov; - - if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { - sd_resolve_query_unref(q); - return -errno; - } - - resolve->n_outstanding++; - - if (_q) - *_q = q; - - return 0; -} - -static int getaddrinfo_done(sd_resolve_query* q) { - assert(q); - assert(q->done); - assert(q->getaddrinfo_handler); - - errno = q->_errno; - h_errno = q->_h_errno; - - return q->getaddrinfo_handler(q, q->ret, q->addrinfo, q->userdata); -} - -_public_ int sd_resolve_getnameinfo( - sd_resolve *resolve, - sd_resolve_query**_q, - const struct sockaddr *sa, socklen_t salen, - int flags, - uint64_t get, - sd_resolve_getnameinfo_handler_t callback, - void *userdata) { - - NameInfoRequest req = {}; - struct msghdr mh = {}; - struct iovec iov[2]; - sd_resolve_query *q; - int r; - - assert_return(resolve, -EINVAL); - assert_return(sa, -EINVAL); - assert_return(salen >= sizeof(struct sockaddr), -EINVAL); - assert_return(salen <= sizeof(union sockaddr_union), -EINVAL); - assert_return((get & ~SD_RESOLVE_GET_BOTH) == 0, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - r = alloc_query(resolve, !_q, &q); - if (r < 0) - return r; - - q->type = REQUEST_NAMEINFO; - q->getnameinfo_handler = callback; - q->userdata = userdata; - - req.header.id = q->id; - req.header.type = REQUEST_NAMEINFO; - req.header.length = sizeof(NameInfoRequest) + salen; - - req.flags = flags; - req.sockaddr_len = salen; - req.gethost = !!(get & SD_RESOLVE_GET_HOST); - req.getserv = !!(get & SD_RESOLVE_GET_SERVICE); - - iov[0] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(NameInfoRequest) }; - iov[1] = (struct iovec) { .iov_base = (void*) sa, .iov_len = salen }; - - mh.msg_iov = iov; - mh.msg_iovlen = 2; - - if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { - sd_resolve_query_unref(q); - return -errno; - } - - resolve->n_outstanding++; - - if (_q) - *_q = q; - - return 0; -} - -static int getnameinfo_done(sd_resolve_query *q) { - - assert(q); - assert(q->done); - assert(q->getnameinfo_handler); - - errno = q->_errno; - h_errno= q->_h_errno; - - return q->getnameinfo_handler(q, q->ret, q->host, q->serv, q->userdata); -} - -_public_ sd_resolve_query* sd_resolve_query_ref(sd_resolve_query *q) { - assert_return(q, NULL); - - assert(q->n_ref >= 1); - q->n_ref++; - - return q; -} - -static void resolve_freeaddrinfo(struct addrinfo *ai) { - while (ai) { - struct addrinfo *next = ai->ai_next; - - free(ai->ai_addr); - free(ai->ai_canonname); - free(ai); - ai = next; - } -} - -static void resolve_query_disconnect(sd_resolve_query *q) { - sd_resolve *resolve; - unsigned i; - - assert(q); - - if (!q->resolve) - return; - - resolve = q->resolve; - assert(resolve->n_queries > 0); - - if (q->done) { - assert(resolve->n_done > 0); - resolve->n_done--; - } - - i = q->id % QUERIES_MAX; - assert(resolve->query_array[i] == q); - resolve->query_array[i] = NULL; - LIST_REMOVE(queries, resolve->queries, q); - resolve->n_queries--; - - q->resolve = NULL; - if (!q->floating) - sd_resolve_unref(resolve); -} - -static void resolve_query_free(sd_resolve_query *q) { - assert(q); - - resolve_query_disconnect(q); - - resolve_freeaddrinfo(q->addrinfo); - free(q->host); - free(q->serv); - free(q); -} - -_public_ sd_resolve_query* sd_resolve_query_unref(sd_resolve_query* q) { - if (!q) - return NULL; - - assert(q->n_ref >= 1); - q->n_ref--; - - if (q->n_ref <= 0) - resolve_query_free(q); - - return NULL; -} - -_public_ int sd_resolve_query_is_done(sd_resolve_query *q) { - assert_return(q, -EINVAL); - assert_return(!resolve_pid_changed(q->resolve), -ECHILD); - - return q->done; -} - -_public_ void* sd_resolve_query_set_userdata(sd_resolve_query *q, void *userdata) { - void *ret; - - assert_return(q, NULL); - assert_return(!resolve_pid_changed(q->resolve), NULL); - - ret = q->userdata; - q->userdata = userdata; - - return ret; -} - -_public_ void* sd_resolve_query_get_userdata(sd_resolve_query *q) { - assert_return(q, NULL); - assert_return(!resolve_pid_changed(q->resolve), NULL); - - return q->userdata; -} - -_public_ sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q) { - assert_return(q, NULL); - assert_return(!resolve_pid_changed(q->resolve), NULL); - - return q->resolve; -} - -static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - sd_resolve *resolve = userdata; - int r; - - assert(resolve); - - r = sd_resolve_process(resolve); - if (r < 0) - return r; - - return 1; -} - -_public_ int sd_resolve_attach_event(sd_resolve *resolve, sd_event *event, int64_t priority) { - int r; - - assert_return(resolve, -EINVAL); - assert_return(!resolve->event, -EBUSY); - - assert(!resolve->event_source); - - if (event) - resolve->event = sd_event_ref(event); - else { - r = sd_event_default(&resolve->event); - if (r < 0) - return r; - } - - r = sd_event_add_io(resolve->event, &resolve->event_source, resolve->fds[RESPONSE_RECV_FD], POLLIN, io_callback, resolve); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(resolve->event_source, priority); - if (r < 0) - goto fail; - - return 0; - -fail: - sd_resolve_detach_event(resolve); - return r; -} - -_public_ int sd_resolve_detach_event(sd_resolve *resolve) { - assert_return(resolve, -EINVAL); - - if (!resolve->event) - return 0; - - if (resolve->event_source) { - sd_event_source_set_enabled(resolve->event_source, SD_EVENT_OFF); - resolve->event_source = sd_event_source_unref(resolve->event_source); - } - - resolve->event = sd_event_unref(resolve->event); - return 1; -} - -_public_ sd_event *sd_resolve_get_event(sd_resolve *resolve) { - assert_return(resolve, NULL); - - return resolve->event; -} diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c deleted file mode 100644 index 1be1a7f8a7..0000000000 --- a/src/libsystemd/sd-resolve/test-resolve.c +++ /dev/null @@ -1,114 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2005-2008 Lennart Poettering - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "sd-resolve.h" - -#include "alloc-util.h" -#include "macro.h" -#include "socket-util.h" -#include "string-util.h" - -static int getaddrinfo_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) { - const struct addrinfo *i; - - assert_se(q); - - if (ret != 0) { - log_error("getaddrinfo error: %s %i", gai_strerror(ret), ret); - return 0; - } - - for (i = ai; i; i = i->ai_next) { - _cleanup_free_ char *addr = NULL; - - assert_se(sockaddr_pretty(i->ai_addr, i->ai_addrlen, false, true, &addr) == 0); - puts(addr); - } - - printf("canonical name: %s\n", strna(ai->ai_canonname)); - - return 0; -} - -static int getnameinfo_handler(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata) { - assert_se(q); - - if (ret != 0) { - log_error("getnameinfo error: %s %i", gai_strerror(ret), ret); - return 0; - } - - printf("Host: %s — Serv: %s\n", strna(host), strna(serv)); - return 0; -} - -int main(int argc, char *argv[]) { - _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q1 = NULL, *q2 = NULL; - _cleanup_(sd_resolve_unrefp) sd_resolve *resolve = NULL; - int r = 0; - - struct addrinfo hints = { - .ai_family = PF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_flags = AI_CANONNAME - }; - - struct sockaddr_in sa = { - .sin_family = AF_INET, - .sin_port = htons(80) - }; - - assert_se(sd_resolve_default(&resolve) >= 0); - - /* Test a floating resolver query */ - sd_resolve_getaddrinfo(resolve, NULL, "redhat.com", "http", NULL, getaddrinfo_handler, NULL); - - /* Make a name -> address query */ - r = sd_resolve_getaddrinfo(resolve, &q1, argc >= 2 ? argv[1] : "www.heise.de", NULL, &hints, getaddrinfo_handler, NULL); - if (r < 0) - log_error_errno(r, "sd_resolve_getaddrinfo(): %m"); - - /* Make an address -> name query */ - sa.sin_addr.s_addr = inet_addr(argc >= 3 ? argv[2] : "193.99.144.71"); - r = sd_resolve_getnameinfo(resolve, &q2, (struct sockaddr*) &sa, sizeof(sa), 0, SD_RESOLVE_GET_BOTH, getnameinfo_handler, NULL); - if (r < 0) - log_error_errno(r, "sd_resolve_getnameinfo(): %m"); - - /* Wait until all queries are completed */ - for (;;) { - r = sd_resolve_wait(resolve, (uint64_t) -1); - if (r == 0) - break; - if (r < 0) { - log_error_errno(r, "sd_resolve_wait(): %m"); - assert_not_reached("sd_resolve_wait() failed"); - } - } - - return 0; -} diff --git a/src/libsystemd/sd-utf8/Makefile b/src/libsystemd/sd-utf8/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-utf8/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../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 deleted file mode 100644 index 33a5a04ea1..0000000000 --- a/src/libsystemd/sd-utf8/sd-utf8.c +++ /dev/null @@ -1,35 +0,0 @@ -/*** - 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 . -***/ - -#include "sd-utf8.h" - -#include "utf8.h" -#include "util.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/src/Makefile b/src/libsystemd/src/Makefile new file mode 100644 index 0000000000..dceadf81da --- /dev/null +++ b/src/libsystemd/src/Makefile @@ -0,0 +1,329 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# 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 . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +libsystemd_internal_la_SOURCES = \ + src/systemd/sd-bus.h \ + src/systemd/sd-bus-protocol.h \ + src/systemd/sd-bus-vtable.h \ + src/systemd/sd-utf8.h \ + src/systemd/sd-event.h \ + src/systemd/sd-netlink.h \ + src/systemd/sd-resolve.h \ + src/systemd/sd-login.h \ + src/systemd/sd-id128.h \ + src/systemd/sd-daemon.h \ + src/systemd/sd-path.h \ + src/systemd/sd-network.h \ + src/systemd/sd-hwdb.h \ + src/systemd/sd-device.h \ + src/libsystemd/libsystemd.sym \ + 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-common-errors.h \ + src/libsystemd/sd-bus/bus-common-errors.c \ + 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-track.c \ + src/libsystemd/sd-bus/bus-track.h \ + src/libsystemd/sd-bus/bus-slot.c \ + src/libsystemd/sd-bus/bus-slot.h \ + src/libsystemd/sd-bus/bus-protocol.h \ + src/libsystemd/sd-bus/kdbus.h \ + src/libsystemd/sd-bus/bus-dump.c \ + src/libsystemd/sd-bus/bus-dump.h \ + src/libsystemd/sd-utf8/sd-utf8.c \ + src/libsystemd/sd-event/sd-event.c \ + src/libsystemd/sd-netlink/sd-netlink.c \ + src/libsystemd/sd-netlink/netlink-internal.h \ + src/libsystemd/sd-netlink/netlink-message.c \ + src/libsystemd/sd-netlink/netlink-socket.c \ + src/libsystemd/sd-netlink/rtnl-message.c \ + src/libsystemd/sd-netlink/netlink-types.h \ + src/libsystemd/sd-netlink/netlink-types.c \ + src/libsystemd/sd-netlink/netlink-util.h \ + src/libsystemd/sd-netlink/netlink-util.c \ + src/libsystemd/sd-netlink/local-addresses.h \ + src/libsystemd/sd-netlink/local-addresses.c \ + src/libsystemd/sd-id128/sd-id128.c \ + src/libsystemd/sd-daemon/sd-daemon.c \ + src/libsystemd/sd-login/sd-login.c \ + src/libsystemd/sd-path/sd-path.c \ + src/libsystemd/sd-network/sd-network.c \ + src/libsystemd/sd-network/network-util.h \ + src/libsystemd/sd-network/network-util.c \ + src/libsystemd/sd-hwdb/sd-hwdb.c \ + src/libsystemd/sd-hwdb/hwdb-util.h \ + src/libsystemd/sd-hwdb/hwdb-internal.h \ + src/libsystemd/sd-device/device-internal.h \ + src/libsystemd/sd-device/device-util.h \ + src/libsystemd/sd-device/device-enumerator.c \ + src/libsystemd/sd-device/device-enumerator-private.h \ + src/libsystemd/sd-device/sd-device.c \ + src/libsystemd/sd-device/device-private.c \ + src/libsystemd/sd-device/device-private.h \ + src/libsystemd/sd-resolve/sd-resolve.c + +libsystemd_internal_la_LIBADD = \ + libbasic.la \ + -lresolv + +noinst_LTLIBRARIES += \ + libsystemd-internal.la + +test_bus_marshal_SOURCES = \ + src/libsystemd/sd-bus/test-bus-marshal.c + +test_bus_marshal_LDADD = \ + libshared.la \ + $(GLIB_LIBS) \ + $(DBUS_LIBS) + +test_bus_marshal_CFLAGS = \ + $(AM_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) + +test_bus_signature_SOURCES = \ + src/libsystemd/sd-bus/test-bus-signature.c + +test_bus_signature_LDADD = \ + libshared.la + +test_bus_chat_SOURCES = \ + src/libsystemd/sd-bus/test-bus-chat.c + +test_bus_chat_LDADD = \ + libshared.la + +test_bus_cleanup_SOURCES = \ + src/libsystemd/sd-bus/test-bus-cleanup.c + +test_bus_cleanup_CFLAGS = \ + $(AM_CFLAGS) \ + $(SECCOMP_CFLAGS) + +test_bus_cleanup_LDADD = \ + libshared.la + +test_bus_server_SOURCES = \ + src/libsystemd/sd-bus/test-bus-server.c + +test_bus_server_LDADD = \ + libshared.la + +test_bus_objects_SOURCES = \ + src/libsystemd/sd-bus/test-bus-objects.c + +test_bus_objects_LDADD = \ + libshared.la + +test_bus_error_SOURCES = \ + src/libsystemd/sd-bus/test-bus-error.c + +test_bus_error_LDADD = \ + libshared.la + +test_bus_gvariant_SOURCES = \ + src/libsystemd/sd-bus/test-bus-gvariant.c + +test_bus_gvariant_LDADD = \ + libshared.la \ + $(GLIB_LIBS) + +test_bus_gvariant_CFLAGS = \ + $(AM_CFLAGS) \ + $(GLIB_CFLAGS) + +test_bus_creds_SOURCES = \ + src/libsystemd/sd-bus/test-bus-creds.c + +test_bus_creds_LDADD = \ + libshared.la + +test_bus_match_SOURCES = \ + src/libsystemd/sd-bus/test-bus-match.c + +test_bus_match_LDADD = \ + libshared.la + +test_bus_kernel_SOURCES = \ + src/libsystemd/sd-bus/test-bus-kernel.c + +test_bus_kernel_LDADD = \ + libshared.la + +test_bus_kernel_bloom_SOURCES = \ + src/libsystemd/sd-bus/test-bus-kernel-bloom.c + +test_bus_kernel_bloom_LDADD = \ + libshared.la + +test_bus_benchmark_SOURCES = \ + src/libsystemd/sd-bus/test-bus-benchmark.c + +test_bus_benchmark_LDADD = \ + libshared.la + +test_bus_zero_copy_SOURCES = \ + src/libsystemd/sd-bus/test-bus-zero-copy.c + +test_bus_zero_copy_LDADD = \ + libshared.la + +test_bus_introspect_SOURCES = \ + src/libsystemd/sd-bus/test-bus-introspect.c + +test_bus_introspect_LDADD = \ + libshared.la + +test_event_SOURCES = \ + src/libsystemd/sd-event/test-event.c + +test_event_LDADD = \ + libshared.la + +test_netlink_SOURCES = \ + src/libsystemd/sd-netlink/test-netlink.c + +test_netlink_LDADD = \ + libshared.la + +test_local_addresses_SOURCES = \ + src/libsystemd/sd-netlink/test-local-addresses.c + +test_local_addresses_LDADD = \ + libshared.la + +test_resolve_SOURCES = \ + src/libsystemd/sd-resolve/test-resolve.c + +test_resolve_LDADD = \ + libshared.la + +pkginclude_HEADERS += \ + src/systemd/sd-journal.h \ + src/systemd/sd-messages.h \ + src/systemd/_sd-common.h + +libsystemd_journal_internal_la_SOURCES = \ + src/journal/sd-journal.c \ + src/systemd/sd-journal.h \ + src/systemd/_sd-common.h \ + src/journal/journal-file.c \ + src/journal/journal-file.h \ + src/journal/journal-vacuum.c \ + src/journal/journal-vacuum.h \ + src/journal/journal-verify.c \ + src/journal/journal-verify.h \ + src/journal/lookup3.c \ + src/journal/lookup3.h \ + src/journal/journal-send.c \ + src/journal/journal-def.h \ + src/journal/compress.h \ + src/journal/catalog.c \ + src/journal/catalog.h \ + src/journal/mmap-cache.c \ + src/journal/mmap-cache.h \ + src/journal/compress.c \ + src/journal/audit-type.h \ + src/journal/audit-type.c \ + src/shared/gcrypt-util.h \ + src/shared/gcrypt-util.c + +nodist_libsystemd_journal_internal_la_SOURCES = \ + src/journal/audit_type-to-name.h + +gperf_txt_sources += \ + src/journal/audit_type-list.txt + +# using _CFLAGS = in the conditional below would suppress AM_CFLAGS +libsystemd_journal_internal_la_CFLAGS = \ + $(AM_CFLAGS) + +libsystemd_journal_internal_la_LIBADD = + +ifneq ($(HAVE_XZ),) +libsystemd_journal_internal_la_CFLAGS += \ + $(XZ_CFLAGS) + +libsystemd_journal_internal_la_LIBADD += \ + $(XZ_LIBS) +endif # HAVE_XZ + +ifneq ($(HAVE_LZ4),) +libsystemd_journal_internal_la_CFLAGS += \ + $(LZ4_CFLAGS) + +libsystemd_journal_internal_la_LIBADD += \ + $(LZ4_LIBS) +endif # HAVE_LZ4 + +ifneq ($(HAVE_GCRYPT),) +libsystemd_journal_internal_la_SOURCES += \ + src/journal/journal-authenticate.c \ + src/journal/journal-authenticate.h \ + src/journal/fsprg.c \ + src/journal/fsprg.h + +libsystemd_journal_internal_la_LIBADD += \ + $(GCRYPT_LIBS) + +# fsprg.c is a drop-in file using void pointer arithmetic +libsystemd_journal_internal_la_CFLAGS += \ + $(GCRYPT_CFLAGS) \ + -Wno-pointer-arith +endif # HAVE_GCRYPT + +noinst_LTLIBRARIES += \ + libsystemd-journal-internal.la +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libsystemd/src/sd-bus/DIFFERENCES b/src/libsystemd/src/sd-bus/DIFFERENCES new file mode 100644 index 0000000000..db269675a7 --- /dev/null +++ b/src/libsystemd/src/sd-bus/DIFFERENCES @@ -0,0 +1,25 @@ +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. + +- NameOwnerChanged is a synthetic message, generated locally and not + by the driver. On dbus1 only the Disconnected message was + synthesized like this. + +- There's no standard per-session bus anymore. Only a per-user bus. diff --git a/src/libsystemd/src/sd-bus/GVARIANT-SERIALIZATION b/src/libsystemd/src/sd-bus/GVARIANT-SERIALIZATION new file mode 100644 index 0000000000..6aeb11364a --- /dev/null +++ b/src/libsystemd/src/sd-bus/GVARIANT-SERIALIZATION @@ -0,0 +1,110 @@ +How we use GVariant for serializing D-Bus messages +-------------------------------------------------- + +We stay close to the original dbus1 framing as possible, but make +certain changes to adapt for GVariant. 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 32bit Serial number + + = 12 bytes + +This header is then followed by the fields array, whose first value is +a 32bit array size. + +When using GVariant we keep the basic structure in place, only +slightly alter 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 Reserved, must be 0 + t 64bit Cookie + + = 16 bytes + +This is then followed by the GVariant fields array ("a{tv}"), and +finally the actual body as variant (v). Putting this altogether a +packet on dbus2 hence qualifies as a fully compliant GVariant +structure of (yyyyuta{tv}v). + +For details on gvariant, see: + +https://people.gnome.org/~desrt/gvariant-serialisation.pdf + +Regarding the framing of dbus2, also see: + +https://wiki.gnome.org/Projects/GLib/GDBus/Version2 + +The first four bytes of the header are defined the same way for dbus1 +and dbus2. The first bytes contain the endianess field and the +protocol version, so that the remainder of the message can be safely +made sense of just by looking at the first 32bit. + +Note that the length of the body is no longer included in the header +on dbus2! In fact, the message size must be known in advance, from the +underlying transport in order to parse dbus2 messages, while it is +directly included in dbus1 message headers. This change of semantics +is an effect of GVariant's basic design. + +The serial number has been renamed cookie and has been extended from +32bit to 64bit. It is recommended to avoid the higher 32bit of the +cookie field though, to simplify compatibility with dbus1 peers. Note +that not only the cookie/serial field in the fixed header, but also +the reply_cookie/reply_serial additional header field has been +increased from 32bit to 64bit, too! + +The header field identifiers have been extended from 8bit to +64bit. This has been done to simplify things (as kdbus otherwise uses +exclusively 64bit types, unless there is a strong reason not to), and +has no effect on the serialization size, as due to alignment for each +8bit header field identifier 56 bits of padding had to be added. + +Note that the header size changed, due to these changes. However, +consider that on dbus1 the beginning of the fields array contains the +32bit array size (since that is how arrays are encoded on dbus1), +thus, if one considers that size part of the header, instead of the +array, the size of the header on dbus1 and dbus2 stays identical, at +16 bytes. + + 0 4 8 12 16 + Common: | E | T | F | V | ... + + dbus1: | (as above) | Body Length | Serial | Fields Length | Fields array ... + + gvariant: | (as above) | Reserved | Cookie | Fields array ... + +And that's already it. + +Note: to simplify parsing, valid kdbus/dbus2 messages must include the +entire fixed header and additional header fields in a single non-memfd +message part. Also, the signature string of the body variant all the +way to the end of the message must be in a single non-memfd part +too. The parts for this extended header and footer can be the same +one, and can also continue any amount of additional body bytes. + +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/src/sd-bus/PORTING-DBUS1 b/src/libsystemd/src/sd-bus/PORTING-DBUS1 new file mode 100644 index 0000000000..2dedb28bcf --- /dev/null +++ b/src/libsystemd/src/sd-bus/PORTING-DBUS1 @@ -0,0 +1,535 @@ +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 /sys/fs/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 /sys/fs/kdbus/0-system, and for user +buses the device node is /sys/fs/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. When a +client encountes such an "incompatible" feature it should immediately +try the next bus address configured in the bus address string. + +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 ":1.". + +The kernel will also return the bloom filter size and bloom filter +hash function number 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_FILTER, +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_FDS: for sending over fds attach an item of this type with + an array of fds. + + KDBUS_ITEM_BLOOM_FILTER: the calculated bloom filter of this message, + only for undirected (broadcast) message. + + KDBUS_ITEM_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 basic types + nor arrays of fixed types. The latter is necessary to allow APIs + to return direct pointers to linear arrays of numeric + values. Examples: The basic types "u", "s", "t" have to be in the + same payload item. The array of fixed 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 > 512K (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 > 512K. 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 < 512K, then simply send +the data as PAYLOAD_VEC and reuse the memfd. If it is >= 512K, 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 parameters for the bloom filters that need to be included in +broadcast message is communicated to userspace as part of the hello +response structure (see above). By default it has the parameters m=512 +(bits in the filter), k=8 (nr of hash functions). Note however, that +this is subject to change in later versions, and userspace +implementations must be capable of handling m values between at least +m=8 and m=2^32, and k values between at least k=1 and k=32. The +underlying hash function is SipHash-2-4. It is used with a number of +constant (yet originally randomly generated) 128bit hash keys, more +specifically: + + b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15, + aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b, + 63,fd,ae,be,cd,82,48,12,a1,6e,41,26,cb,fa,a0,c8, + 23,be,45,29,32,d2,46,2d,82,03,52,28,fe,37,17,f5, + 56,3b,bf,ee,5a,4f,43,39,af,aa,94,08,df,f0,fc,10, + 31,80,c8,73,c7,ea,46,d3,aa,25,75,0f,9e,4c,09,29, + 7d,f7,18,4b,7b,a4,44,d5,85,3c,06,e0,65,53,96,6d, + f2,77,e9,6f,93,b5,4e,71,9a,0c,34,88,39,25,bf,35 + +When calculating the first bit index into the bloom filter, the +SipHash-2-4 hash value is calculated for the input data and the first +16 bytes of the array above as hash key. Of the resulting 8 bytes of +output, as many full bytes are taken for the bit index as necessary, +starting from the output's first byte. For the second bit index the +same hash value is used, continuing with the next unused output byte, +and so on. Each time the bytes returned by the hash function are +depleted it is recalculated with the next 16 byte hash key from the +array above and the same input data. + +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). + +If a client connects to a bus whose bloom filter metrics (i.e. filter +size and number of hash functions) are outside of the range the client +supports it must immediately disconnect and continue connection with +the next bus address of the bus connection string. + +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_ID_ADD/KDBUS_ITEM_ID_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 + +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's domain 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=/sys/fs/kdbus/0-system/bus;unix:path=/var/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=/sys/fs/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/src/sd-bus/bus-bloom.c b/src/libsystemd/src/sd-bus/bus-bloom.c new file mode 100644 index 0000000000..112769fcb6 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-bloom.c @@ -0,0 +1,156 @@ +/*** + 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 . +***/ + +#include "bus-bloom.h" +#include "siphash24.h" +#include "util.h" + +static inline void set_bit(uint64_t filter[], unsigned long b) { + filter[b >> 6] |= 1ULL << (b & 63); +} + +static const sd_id128_t hash_keys[] = { + SD_ID128_ARRAY(b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15), + SD_ID128_ARRAY(aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b), + SD_ID128_ARRAY(63,fd,ae,be,cd,82,48,12,a1,6e,41,26,cb,fa,a0,c8), + SD_ID128_ARRAY(23,be,45,29,32,d2,46,2d,82,03,52,28,fe,37,17,f5), + SD_ID128_ARRAY(56,3b,bf,ee,5a,4f,43,39,af,aa,94,08,df,f0,fc,10), + SD_ID128_ARRAY(31,80,c8,73,c7,ea,46,d3,aa,25,75,0f,9e,4c,09,29), + SD_ID128_ARRAY(7d,f7,18,4b,7b,a4,44,d5,85,3c,06,e0,65,53,96,6d), + SD_ID128_ARRAY(f2,77,e9,6f,93,b5,4e,71,9a,0c,34,88,39,25,bf,35), +}; + +static void bloom_add_data( + uint64_t filter[], /* The filter bits */ + size_t size, /* Size of the filter in bytes */ + unsigned k, /* Number of hash functions */ + const void *data, /* Data to hash */ + size_t n) { /* Size of data to hash in bytes */ + + uint64_t h; + uint64_t m; + unsigned w, i, c = 0; + unsigned hash_index; + + assert(size > 0); + assert(k > 0); + + /* Determine bits in filter */ + m = size * 8; + + /* Determine how many bytes we need to generate a bit index 0..m for this filter */ + w = (u64log2(m) + 7) / 8; + + assert(w <= sizeof(uint64_t)); + + /* Make sure we have enough hash keys to generate m * k bits + * of hash value. Note that SipHash24 generates 64 bits of + * hash value for each 128 bits of hash key. */ + assert(k * w <= ELEMENTSOF(hash_keys) * 8); + + for (i = 0, hash_index = 0; i < k; i++) { + uint64_t p = 0; + unsigned d; + + for (d = 0; d < w; d++) { + if (c <= 0) { + h = siphash24(data, n, hash_keys[hash_index++].bytes); + c += 8; + } + + p = (p << 8ULL) | (uint64_t) ((uint8_t *)&h)[8 - c]; + c--; + } + + p &= m - 1; + set_bit(filter, p); + } + + /* log_debug("bloom: adding <%.*s>", (int) n, (char*) data); */ +} + +void bloom_add_pair(uint64_t filter[], size_t size, unsigned k, 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, size, k, c, n); +} + +void bloom_add_prefixes(uint64_t filter[], size_t size, unsigned k, 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); + + bloom_add_data(filter, size, k, c, n); + + for (;;) { + char *e; + + e = strrchr(p, sep); + if (!e) + break; + + *(e + 1) = 0; + bloom_add_data(filter, size, k, c, e - c + 1); + + if (e == p) + break; + + *e = 0; + bloom_add_data(filter, size, k, c, e - c); + } +} + +bool bloom_validate_parameters(size_t size, unsigned k) { + uint64_t m; + unsigned w; + + if (size <= 0) + return false; + + if (k <= 0) + return false; + + m = size * 8; + w = (u64log2(m) + 7) / 8; + if (w > sizeof(uint64_t)) + return false; + + if (k * w > ELEMENTSOF(hash_keys) * 8) + return false; + + return true; +} diff --git a/src/libsystemd/src/sd-bus/bus-bloom.h b/src/libsystemd/src/sd-bus/bus-bloom.h new file mode 100644 index 0000000000..c824622b95 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-bloom.h @@ -0,0 +1,43 @@ +#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 . +***/ + +#include +#include +#include + +/* + * Our default bloom filter has the following parameters: + * + * m=512 (bits in the filter) + * k=8 (hash functions) + * + * We use SipHash24 as hash function with a number of (originally + * randomized) but fixed hash keys. + * + */ + +#define DEFAULT_BLOOM_SIZE (512/8) /* m: filter size */ +#define DEFAULT_BLOOM_N_HASH 8 /* k: number of hash functions */ + +void bloom_add_pair(uint64_t filter[], size_t size, unsigned n_hash, const char *a, const char *b); +void bloom_add_prefixes(uint64_t filter[], size_t size, unsigned n_hash, const char *a, const char *b, char sep); + +bool bloom_validate_parameters(size_t size, unsigned n_hash); diff --git a/src/libsystemd/src/sd-bus/bus-common-errors.c b/src/libsystemd/src/sd-bus/bus-common-errors.c new file mode 100644 index 0000000000..a19e98e94b --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-common-errors.c @@ -0,0 +1,87 @@ +/*** + This file is part of systemd. + + Copyright 2014 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 . +***/ + +#include + +#include + +#include "bus-common-errors.h" +#include "bus-error.h" + +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_UNIT, ENOENT), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_UNIT_FOR_PID, ESRCH), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_EXISTS, EEXIST), + SD_BUS_ERROR_MAP(BUS_ERROR_LOAD_FAILED, EIO), + SD_BUS_ERROR_MAP(BUS_ERROR_JOB_FAILED, EREMOTEIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_JOB, ENOENT), + SD_BUS_ERROR_MAP(BUS_ERROR_NOT_SUBSCRIBED, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_ALREADY_SUBSCRIBED, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_ONLY_BY_DEPENDENCY, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, EDEADLK), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLK), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLK), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ESHUTDOWN), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_GENERATED, EADDRNOTAVAIL), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_LINKED, ELOOP), + SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM), + SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED), + SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_MACHINE_FOR_PID, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_MACHINE_EXISTS, EEXIST), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRIVATE_NETWORKING, ENOSYS), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SESSION, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SESSION_FOR_PID, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_USER, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_USER_FOR_PID, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SEAT, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_SESSION_NOT_ON_SEAT, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_NOT_IN_CONTROL, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_IS_TAKEN, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_NOT_TAKEN, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_OPERATION_IN_PROGRESS, EINPROGRESS), + SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP), + + SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_PROCESS, ESRCH), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_NAME_SERVERS, ESRCH), + SD_BUS_ERROR_MAP(BUS_ERROR_INVALID_REPLY, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_RR, ENOENT), + SD_BUS_ERROR_MAP(BUS_ERROR_CNAME_LOOP, EDEADLK), + SD_BUS_ERROR_MAP(BUS_ERROR_ABORTED, ECANCELED), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SERVICE, EUNATCH), + SD_BUS_ERROR_MAP(BUS_ERROR_DNSSEC_FAILED, EHOSTUNREACH), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_TRUST_ANCHOR, EHOSTUNREACH), + SD_BUS_ERROR_MAP(BUS_ERROR_RR_TYPE_UNSUPPORTED, EOPNOTSUPP), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_LINK, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY), + SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY), + + SD_BUS_ERROR_MAP_END +}; diff --git a/src/libsystemd/src/sd-bus/bus-common-errors.h b/src/libsystemd/src/sd-bus/bus-common-errors.h new file mode 100644 index 0000000000..c8f369cb78 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-common-errors.h @@ -0,0 +1,86 @@ +#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 . +***/ + +#include "bus-error.h" + +#define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit" +#define BUS_ERROR_NO_UNIT_FOR_PID "org.freedesktop.systemd1.NoUnitForPID" +#define BUS_ERROR_UNIT_EXISTS "org.freedesktop.systemd1.UnitExists" +#define BUS_ERROR_LOAD_FAILED "org.freedesktop.systemd1.LoadFailed" +#define BUS_ERROR_JOB_FAILED "org.freedesktop.systemd1.JobFailed" +#define BUS_ERROR_NO_SUCH_JOB "org.freedesktop.systemd1.NoSuchJob" +#define BUS_ERROR_NOT_SUBSCRIBED "org.freedesktop.systemd1.NotSubscribed" +#define BUS_ERROR_ALREADY_SUBSCRIBED "org.freedesktop.systemd1.AlreadySubscribed" +#define BUS_ERROR_ONLY_BY_DEPENDENCY "org.freedesktop.systemd1.OnlyByDependency" +#define BUS_ERROR_TRANSACTION_JOBS_CONFLICTING "org.freedesktop.systemd1.TransactionJobsConflicting" +#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic" +#define BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE "org.freedesktop.systemd1.TransactionIsDestructive" +#define BUS_ERROR_UNIT_MASKED "org.freedesktop.systemd1.UnitMasked" +#define BUS_ERROR_UNIT_GENERATED "org.freedesktop.systemd1.UnitGenerated" +#define BUS_ERROR_UNIT_LINKED "org.freedesktop.systemd1.UnitLinked" +#define BUS_ERROR_JOB_TYPE_NOT_APPLICABLE "org.freedesktop.systemd1.JobTypeNotApplicable" +#define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation" +#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown" +#define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning" + +#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine" +#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage" +#define BUS_ERROR_NO_MACHINE_FOR_PID "org.freedesktop.machine1.NoMachineForPID" +#define BUS_ERROR_MACHINE_EXISTS "org.freedesktop.machine1.MachineExists" +#define BUS_ERROR_NO_PRIVATE_NETWORKING "org.freedesktop.machine1.NoPrivateNetworking" +#define BUS_ERROR_NO_SUCH_USER_MAPPING "org.freedesktop.machine1.NoSuchUserMapping" +#define BUS_ERROR_NO_SUCH_GROUP_MAPPING "org.freedesktop.machine1.NoSuchGroupMapping" + +#define BUS_ERROR_NO_SUCH_SESSION "org.freedesktop.login1.NoSuchSession" +#define BUS_ERROR_NO_SESSION_FOR_PID "org.freedesktop.login1.NoSessionForPID" +#define BUS_ERROR_NO_SUCH_USER "org.freedesktop.login1.NoSuchUser" +#define BUS_ERROR_NO_USER_FOR_PID "org.freedesktop.login1.NoUserForPID" +#define BUS_ERROR_NO_SUCH_SEAT "org.freedesktop.login1.NoSuchSeat" +#define BUS_ERROR_SESSION_NOT_ON_SEAT "org.freedesktop.login1.SessionNotOnSeat" +#define BUS_ERROR_NOT_IN_CONTROL "org.freedesktop.login1.NotInControl" +#define BUS_ERROR_DEVICE_IS_TAKEN "org.freedesktop.login1.DeviceIsTaken" +#define BUS_ERROR_DEVICE_NOT_TAKEN "org.freedesktop.login1.DeviceNotTaken" +#define BUS_ERROR_OPERATION_IN_PROGRESS "org.freedesktop.login1.OperationInProgress" +#define BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED "org.freedesktop.login1.SleepVerbNotSupported" +#define BUS_ERROR_SESSION_BUSY "org.freedesktop.login1.SessionBusy" + +#define BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED "org.freedesktop.timedate1.AutomaticTimeSyncEnabled" + +#define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess" + +#define BUS_ERROR_NO_NAME_SERVERS "org.freedesktop.resolve1.NoNameServers" +#define BUS_ERROR_INVALID_REPLY "org.freedesktop.resolve1.InvalidReply" +#define BUS_ERROR_NO_SUCH_RR "org.freedesktop.resolve1.NoSuchRR" +#define BUS_ERROR_CNAME_LOOP "org.freedesktop.resolve1.CNameLoop" +#define BUS_ERROR_ABORTED "org.freedesktop.resolve1.Aborted" +#define BUS_ERROR_NO_SUCH_SERVICE "org.freedesktop.resolve1.NoSuchService" +#define BUS_ERROR_DNSSEC_FAILED "org.freedesktop.resolve1.DnssecFailed" +#define BUS_ERROR_NO_TRUST_ANCHOR "org.freedesktop.resolve1.NoTrustAnchor" +#define BUS_ERROR_RR_TYPE_UNSUPPORTED "org.freedesktop.resolve1.ResourceRecordTypeUnsupported" +#define BUS_ERROR_NO_SUCH_LINK "org.freedesktop.resolve1.NoSuchLink" +#define BUS_ERROR_LINK_BUSY "org.freedesktop.resolve1.LinkBusy" +#define BUS_ERROR_NETWORK_DOWN "org.freedesktop.resolve1.NetworkDown" +#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError." + +#define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer" +#define BUS_ERROR_TRANSFER_IN_PROGRESS "org.freedesktop.import1.TransferInProgress" + +BUS_ERROR_MAP_ELF_USE(bus_common_errors); diff --git a/src/libsystemd/src/sd-bus/bus-container.c b/src/libsystemd/src/sd-bus/bus-container.c new file mode 100644 index 0000000000..3191d27ded --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-container.c @@ -0,0 +1,277 @@ +/*** + 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 . +***/ + +#include +#include + +#include "bus-container.h" +#include "bus-internal.h" +#include "bus-socket.h" +#include "fd-util.h" +#include "process-util.h" +#include "util.h" + +int bus_container_connect_socket(sd_bus *b) { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; + pid_t child; + siginfo_t si; + int r, error_buf = 0; + ssize_t n; + + assert(b); + assert(b->input_fd < 0); + assert(b->output_fd < 0); + assert(b->nspid > 0 || b->machine); + + if (b->nspid <= 0) { + r = container_get_leader(b->machine, &b->nspid); + if (r < 0) + return r; + } + + r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &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); + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return -errno; + + if (child == 0) { + pid_t grandchild; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, 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 SO_PEERCRED of our connection + * comes from a process from within the container, and + * not outside of it */ + + grandchild = fork(); + if (grandchild < 0) + _exit(EXIT_FAILURE); + + if (grandchild == 0) { + + r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); + if (r < 0) { + /* Try to send error up */ + error_buf = errno; + (void) write(pair[1], &error_buf, sizeof(error_buf)); + _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); + } + + pair[1] = safe_close(pair[1]); + + r = wait_for_terminate(child, &si); + if (r < 0) + return r; + + n = read(pair[0], &error_buf, sizeof(error_buf)); + if (n < 0) + return -errno; + + if (n > 0) { + if (n != sizeof(error_buf)) + return -EIO; + + if (error_buf < 0) + return -EIO; + + if (error_buf == EINPROGRESS) + return 1; + + if (error_buf > 0) + return -error_buf; + } + + if (si.si_code != CLD_EXITED) + return -EIO; + + if (si.si_status != EXIT_SUCCESS) + return -EIO; + + return bus_socket_start_auth(b); +} + +int bus_container_connect_kernel(sd_bus *b) { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + int error_buf = 0; + struct iovec iov = { + .iov_base = &error_buf, + .iov_len = sizeof(error_buf), + }; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + struct cmsghdr *cmsg; + pid_t child; + siginfo_t si; + int r, fd = -1; + ssize_t n; + + assert(b); + assert(b->input_fd < 0); + assert(b->output_fd < 0); + assert(b->nspid > 0 || b->machine); + + if (b->nspid <= 0) { + r = container_get_leader(b->machine, &b->nspid); + if (r < 0) + return r; + } + + r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return -errno; + + if (child == 0) { + pid_t grandchild; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, 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) { + /* Try to send error up */ + error_buf = errno; + (void) write(pair[1], &error_buf, sizeof(error_buf)); + _exit(EXIT_FAILURE); + } + + r = send_one_fd(pair[1], fd, 0); + if (r < 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); + } + + pair[1] = safe_close(pair[1]); + + r = wait_for_terminate(child, &si); + if (r < 0) + return r; + + n = recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); + if (n < 0) + return -errno; + + CMSG_FOREACH(cmsg, &mh) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + int *fds; + unsigned n_fds; + + assert(fd < 0); + + 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]; + } + } + + /* If there's an fd passed, we are good. */ + if (fd >= 0) { + b->input_fd = b->output_fd = fd; + return bus_kernel_take_fd(b); + } + + /* If there's an error passed, use it */ + if (n == sizeof(error_buf) && error_buf > 0) + return -error_buf; + + /* Otherwise, we have no clue */ + return -EIO; +} diff --git a/src/libsystemd/src/sd-bus/bus-container.h b/src/libsystemd/src/sd-bus/bus-container.h new file mode 100644 index 0000000000..5cd6d15ede --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-container.h @@ -0,0 +1,25 @@ +#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 . +***/ + +#include + +int bus_container_connect_socket(sd_bus *b); +int bus_container_connect_kernel(sd_bus *b); diff --git a/src/libsystemd/src/sd-bus/bus-control.c b/src/libsystemd/src/sd-bus/bus-control.c new file mode 100644 index 0000000000..00de530d58 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-control.c @@ -0,0 +1,1588 @@ +/*** + 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 . +***/ + +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include +#endif + +#include +#include + +#include + +#include "alloc-util.h" +#include "bus-bloom.h" +#include "bus-control.h" +#include "bus-internal.h" +#include "bus-message.h" +#include "bus-util.h" +#include "capability-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "strv.h" +#include "user-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); + + if (!bus->bus_client) + return -EINVAL; + + 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 *n; + size_t size, l; + int r; + + assert(bus); + assert(name); + + l = strlen(name) + 1; + size = offsetof(struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(l); + n = alloca0_align(size, 8); + n->size = size; + n->flags = request_name_flags_to_kdbus(flags); + + n->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; + n->items[0].type = KDBUS_ITEM_NAME; + memcpy(n->items[0].str, name, l); + +#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->return_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_(sd_bus_message_unrefp) 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_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->bus_client) + return -EINVAL; + + /* Don't allow requesting the special driver and local names */ + if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + 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 *n; + size_t size, l; + int r; + + assert(bus); + assert(name); + + l = strlen(name) + 1; + size = offsetof(struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(l); + n = alloca0_align(size, 8); + n->size = size; + + n->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; + n->items[0].type = KDBUS_ITEM_NAME; + memcpy(n->items[0].str, name, l); + +#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 0; +} + +static int bus_release_name_dbus1(sd_bus *bus, const char *name) { + _cleanup_(sd_bus_message_unrefp) 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_pid_changed(bus), -ECHILD); + assert_return(service_name_is_valid(name), -EINVAL); + assert_return(name[0] != ':', -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + /* Don't allow releasing the special driver and local names */ + if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + 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_list cmd = { + .size = sizeof(cmd), + .flags = flags, + }; + struct kdbus_info *name_list, *name; + uint64_t previous_id = 0; + int r; + + /* Caller will free half-constructed list on failure... */ + + r = ioctl(bus->input_fd, KDBUS_CMD_LIST, &cmd); + if (r < 0) + return -errno; + + name_list = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd.offset); + + KDBUS_FOREACH(name, name_list, cmd.list_size) { + struct kdbus_item *item; + + if ((flags & KDBUS_LIST_UNIQUE) && name->id != previous_id && !(name->flags & KDBUS_HELLO_ACTIVATOR)) { + char *n; + + if (asprintf(&n, ":1.%llu", (unsigned long long) name->id) < 0) { + r = -ENOMEM; + goto fail; + } + + r = strv_consume(x, n); + if (r < 0) + goto fail; + + previous_id = name->id; + } + + KDBUS_ITEM_FOREACH(item, name, items) { + if (item->type == KDBUS_ITEM_OWNED_NAME) { + if (service_name_is_valid(item->name.name)) { + r = strv_extend(x, item->name.name); + if (r < 0) { + r = -ENOMEM; + goto fail; + } + } + } + } + } + + r = 0; + +fail: + bus_kernel_cmd_free(bus, cmd.offset); + return r; +} + +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_LIST_UNIQUE | KDBUS_LIST_NAMES, &x); + if (r < 0) + return r; + } + + if (activatable) { + r = kernel_get_list(bus, KDBUS_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_(sd_bus_message_unrefp) 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_pid_changed(bus), -ECHILD); + + if (!bus->bus_client) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (bus->is_kernel) + return bus_list_names_kernel(bus, acquired, activatable); + else + return bus_list_names_dbus1(bus, acquired, activatable); +} + +static int bus_populate_creds_from_items( + sd_bus *bus, + struct kdbus_info *info, + uint64_t mask, + sd_bus_creds *c) { + + struct kdbus_item *item; + uint64_t m; + int r; + + assert(bus); + assert(info); + assert(c); + + KDBUS_ITEM_FOREACH(item, info, items) { + + switch (item->type) { + + case KDBUS_ITEM_PIDS: + + if (mask & SD_BUS_CREDS_PID && item->pids.pid > 0) { + c->pid = (pid_t) item->pids.pid; + c->mask |= SD_BUS_CREDS_PID; + } + + if (mask & SD_BUS_CREDS_TID && item->pids.tid > 0) { + c->tid = (pid_t) item->pids.tid; + c->mask |= SD_BUS_CREDS_TID; + } + + if (mask & SD_BUS_CREDS_PPID) { + if (item->pids.ppid > 0) { + c->ppid = (pid_t) item->pids.ppid; + c->mask |= SD_BUS_CREDS_PPID; + } else if (item->pids.pid == 1) { + /* The structure doesn't + * really distinguish the case + * where a process has no + * parent and where we don't + * know it because it could + * not be translated due to + * namespaces. However, we + * know that PID 1 has no + * parent process, hence let's + * patch that in, manually. */ + c->ppid = 0; + c->mask |= SD_BUS_CREDS_PPID; + } + } + + break; + + case KDBUS_ITEM_CREDS: + + if (mask & SD_BUS_CREDS_UID && (uid_t) item->creds.uid != UID_INVALID) { + c->uid = (uid_t) item->creds.uid; + c->mask |= SD_BUS_CREDS_UID; + } + + if (mask & SD_BUS_CREDS_EUID && (uid_t) item->creds.euid != UID_INVALID) { + c->euid = (uid_t) item->creds.euid; + c->mask |= SD_BUS_CREDS_EUID; + } + + if (mask & SD_BUS_CREDS_SUID && (uid_t) item->creds.suid != UID_INVALID) { + c->suid = (uid_t) item->creds.suid; + c->mask |= SD_BUS_CREDS_SUID; + } + + if (mask & SD_BUS_CREDS_FSUID && (uid_t) item->creds.fsuid != UID_INVALID) { + c->fsuid = (uid_t) item->creds.fsuid; + c->mask |= SD_BUS_CREDS_FSUID; + } + + if (mask & SD_BUS_CREDS_GID && (gid_t) item->creds.gid != GID_INVALID) { + c->gid = (gid_t) item->creds.gid; + c->mask |= SD_BUS_CREDS_GID; + } + + if (mask & SD_BUS_CREDS_EGID && (gid_t) item->creds.egid != GID_INVALID) { + c->egid = (gid_t) item->creds.egid; + c->mask |= SD_BUS_CREDS_EGID; + } + + if (mask & SD_BUS_CREDS_SGID && (gid_t) item->creds.sgid != GID_INVALID) { + c->sgid = (gid_t) item->creds.sgid; + c->mask |= SD_BUS_CREDS_SGID; + } + + if (mask & SD_BUS_CREDS_FSGID && (gid_t) item->creds.fsgid != GID_INVALID) { + c->fsgid = (gid_t) item->creds.fsgid; + c->mask |= SD_BUS_CREDS_FSGID; + } + + break; + + case KDBUS_ITEM_PID_COMM: + if (mask & SD_BUS_CREDS_COMM) { + r = free_and_strdup(&c->comm, item->str); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_COMM; + } + break; + + case KDBUS_ITEM_TID_COMM: + if (mask & SD_BUS_CREDS_TID_COMM) { + r = free_and_strdup(&c->tid_comm, item->str); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_TID_COMM; + } + break; + + case KDBUS_ITEM_EXE: + if (mask & SD_BUS_CREDS_EXE) { + r = free_and_strdup(&c->exe, item->str); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_EXE; + } + break; + + case KDBUS_ITEM_CMDLINE: + if (mask & SD_BUS_CREDS_CMDLINE) { + c->cmdline_size = item->size - offsetof(struct kdbus_item, data); + c->cmdline = memdup(item->data, c->cmdline_size); + if (!c->cmdline) + return -ENOMEM; + + 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) { + r = free_and_strdup(&c->cgroup, item->str); + if (r < 0) + return r; + + r = bus_get_root_path(bus); + if (r < 0) + return r; + + r = free_and_strdup(&c->cgroup_root, bus->cgroup_root); + if (r < 0) + return r; + + 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) { + if (item->caps.last_cap != cap_last_cap() || + item->size - offsetof(struct kdbus_item, caps.caps) < DIV_ROUND_UP(item->caps.last_cap, 32U) * 4 * 4) + return -EBADMSG; + + c->capability = memdup(item->caps.caps, item->size - offsetof(struct kdbus_item, caps.caps)); + if (!c->capability) + return -ENOMEM; + + c->mask |= m; + } + break; + + case KDBUS_ITEM_SECLABEL: + if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) { + r = free_and_strdup(&c->label, item->str); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + } + break; + + case KDBUS_ITEM_AUDIT: + if (mask & SD_BUS_CREDS_AUDIT_SESSION_ID) { + c->audit_session_id = (uint32_t) item->audit.sessionid; + c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; + } + + if (mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) { + c->audit_login_uid = (uid_t) item->audit.loginuid; + c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; + } + break; + + case KDBUS_ITEM_OWNED_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) + return r; + + c->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; + } + break; + + case KDBUS_ITEM_CONN_DESCRIPTION: + if (mask & SD_BUS_CREDS_DESCRIPTION) { + r = free_and_strdup(&c->description, item->str); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_DESCRIPTION; + } + break; + + case KDBUS_ITEM_AUXGROUPS: + if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + size_t i, n; + uid_t *g; + + n = (item->size - offsetof(struct kdbus_item, data64)) / sizeof(uint64_t); + g = new(gid_t, n); + if (!g) + return -ENOMEM; + + for (i = 0; i < n; i++) + g[i] = item->data64[i]; + + free(c->supplementary_gids); + c->supplementary_gids = g; + c->n_supplementary_gids = n; + + c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + } + break; + } + } + + return 0; +} + +int bus_get_name_creds_kdbus( + sd_bus *bus, + const char *name, + uint64_t mask, + bool allow_activator, + sd_bus_creds **creds) { + + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; + struct kdbus_cmd_info *cmd; + struct kdbus_info *conn_info; + size_t size, l; + uint64_t id; + int r; + + if (streq(name, "org.freedesktop.DBus")) + return -EOPNOTSUPP; + + r = bus_kernel_parse_unique_name(name, &id); + if (r < 0) + return r; + if (r > 0) { + size = offsetof(struct kdbus_cmd_info, items); + cmd = alloca0_align(size, 8); + cmd->id = id; + } else { + l = strlen(name) + 1; + size = offsetof(struct kdbus_cmd_info, items) + KDBUS_ITEM_SIZE(l); + cmd = alloca0_align(size, 8); + cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; + cmd->items[0].type = KDBUS_ITEM_NAME; + memcpy(cmd->items[0].str, name, l); + } + + /* If augmentation is on, and the bus didn't provide us + * the bits we want, then ask for the PID/TID so that we + * can read the rest from /proc. */ + if ((mask & SD_BUS_CREDS_AUGMENT) && + (mask & (SD_BUS_CREDS_PPID| + SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS| + SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_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_SELINUX_CONTEXT| + SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))) + mask |= SD_BUS_CREDS_PID; + + cmd->size = size; + cmd->attach_flags = attach_flags_to_kdbus(mask); + + r = ioctl(bus->input_fd, KDBUS_CMD_CONN_INFO, cmd); + if (r < 0) + return -errno; + + conn_info = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd->offset); + + /* Non-activated names are considered not available */ + if (!allow_activator && (conn_info->flags & KDBUS_HELLO_ACTIVATOR)) { + if (name[0] == ':') + r = -ENXIO; + else + r = -ESRCH; + goto fail; + } + + c = bus_creds_new(); + if (!c) { + r = -ENOMEM; + goto fail; + } + + if (mask & SD_BUS_CREDS_UNIQUE_NAME) { + if (asprintf(&c->unique_name, ":1.%llu", (unsigned long long) conn_info->id) < 0) { + r = -ENOMEM; + goto fail; + } + + c->mask |= SD_BUS_CREDS_UNIQUE_NAME; + } + + /* If KDBUS_ITEM_OWNED_NAME is requested then we'll get 0 of + them in case the service has no names. This does not mean + however that the list of owned names could not be + acquired. Hence, let's explicitly clarify that the data is + complete. */ + c->mask |= mask & SD_BUS_CREDS_WELL_KNOWN_NAMES; + + r = bus_populate_creds_from_items(bus, conn_info, mask, c); + if (r < 0) + goto fail; + + r = bus_creds_add_more(c, mask, 0, 0); + if (r < 0) + goto fail; + + if (creds) { + *creds = c; + c = NULL; + } + + r = 0; + +fail: + bus_kernel_cmd_free(bus, cmd->offset); + return r; +} + +static int bus_get_name_creds_dbus1( + sd_bus *bus, + const char *name, + uint64_t mask, + sd_bus_creds **creds) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_unique = NULL, *reply = NULL; + _cleanup_(sd_bus_creds_unrefp) 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) || + ((mask & SD_BUS_CREDS_AUGMENT) && + (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS| + 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_SELINUX_CONTEXT| + 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_EUID) { + 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->euid = u; + c->mask |= SD_BUS_CREDS_EUID; + + reply = sd_bus_message_unref(reply); + } + + if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const void *p = NULL; + size_t sz = 0; + + r = sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetConnectionSELinuxSecurityContext", + &error, + &reply, + "s", + unique ? unique : name); + if (r < 0) { + if (!sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown")) + return r; + } else { + 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_name_creds( + 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_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); + assert_return(mask == 0 || creds, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(service_name_is_valid(name), -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + if (streq(name, "org.freedesktop.DBus.Local")) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (bus->is_kernel) + return bus_get_name_creds_kdbus(bus, name, mask, false, creds); + else + return bus_get_name_creds_dbus1(bus, name, mask, creds); +} + +static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; + struct kdbus_cmd_info cmd = { + .size = sizeof(struct kdbus_cmd_info), + }; + struct kdbus_info *creator_info; + pid_t pid = 0; + int r; + + c = bus_creds_new(); + if (!c) + return -ENOMEM; + + /* If augmentation is on, and the bus doesn't didn't allow us + * to get the bits we want, then ask for the PID/TID so that we + * can read the rest from /proc. */ + if ((mask & SD_BUS_CREDS_AUGMENT) && + (mask & (SD_BUS_CREDS_PPID| + SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS| + SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_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_SELINUX_CONTEXT| + SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))) + mask |= SD_BUS_CREDS_PID; + + cmd.attach_flags = attach_flags_to_kdbus(mask); + + r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); + if (r < 0) + return -errno; + + creator_info = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd.offset); + + r = bus_populate_creds_from_items(bus, creator_info, mask, c); + bus_kernel_cmd_free(bus, cmd.offset); + if (r < 0) + return r; + + r = bus_creds_add_more(c, mask, pid, 0); + if (r < 0) + return r; + + *ret = c; + c = NULL; + return 0; +} + +static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; + pid_t pid = 0; + bool do_label; + int r; + + assert(bus); + + do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT); + + /* Avoid allocating anything if we have no chance of returning useful data */ + if (!bus->ucred_valid && !do_label) + return -ENODATA; + + c = bus_creds_new(); + if (!c) + return -ENOMEM; + + if (bus->ucred_valid) { + if (bus->ucred.pid > 0) { + pid = c->pid = bus->ucred.pid; + c->mask |= SD_BUS_CREDS_PID & mask; + } + + if (bus->ucred.uid != UID_INVALID) { + c->euid = bus->ucred.uid; + c->mask |= SD_BUS_CREDS_EUID & mask; + } + + if (bus->ucred.gid != GID_INVALID) { + c->egid = bus->ucred.gid; + c->mask |= SD_BUS_CREDS_EGID & mask; + } + } + + if (do_label) { + c->label = strdup(bus->label); + 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; + + *ret = c; + c = NULL; + return 0; +} + +_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { + assert_return(bus, -EINVAL); + assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); + assert_return(ret, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (bus->is_kernel) + return bus_get_owner_creds_kdbus(bus, mask, ret); + else + return bus_get_owner_creds_dbus1(bus, mask, ret); +} + +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_align(sz, 8); + 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.id = old_owner_id; + item->name_change.new_id.id = new_owner_id; + + memcpy_safe(item->name_change.name, name, l); + + /* If the old name is unset or empty, then + * this can match against added names */ + if (isempty(old_owner)) { + 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 (isempty(new_owner)) { + 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_align(sz, 8); + 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 (isempty(old_owner)) { + item->type = KDBUS_ITEM_ID_ADD; + if (!isempty(new_owner)) + item->id_change.id = new_owner_id; + + 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 (isempty(new_owner)) { + item->type = KDBUS_ITEM_ID_REMOVE; + if (!isempty(old_owner)) + item->id_change.id = old_owner_id; + + 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, + struct bus_match_component *components, + unsigned n_components, + uint64_t cookie) { + + struct kdbus_cmd_match *m; + struct kdbus_item *item; + uint64_t *bloom; + size_t sz; + const char *sender = NULL; + size_t sender_length = 0; + uint64_t src_id = KDBUS_MATCH_ID_ANY, dst_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); + + /* Monitor streams don't support matches, make this a NOP */ + if (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + + bloom = alloca0(bus->bloom_size); + + 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, bus->bloom_size, bus->bloom_n_hash, "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, bus->bloom_size, bus->bloom_n_hash, "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, bus->bloom_size, bus->bloom_n_hash, "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, bus->bloom_size, bus->bloom_n_hash, "path", c->value_str); + using_bloom = true; + break; + + case BUS_MATCH_PATH_NAMESPACE: + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "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; + + xsprintf(buf, "arg%i", c->type - BUS_MATCH_ARG); + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); + using_bloom = true; + break; + } + + case BUS_MATCH_ARG_HAS...BUS_MATCH_ARG_HAS_LAST: { + char buf[sizeof("arg")-1 + 2 + sizeof("-has")]; + + xsprintf(buf, "arg%i-has", c->type - BUS_MATCH_ARG_HAS); + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); + using_bloom = true; + break; + } + + case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST: + /* + * XXX: DBus spec defines arg[0..63]path= matching to be + * a two-way glob. That is, if either string is a prefix + * of the other, it matches. + * This is really hard to realize in bloom-filters, as + * we would have to create a bloom-match for each prefix + * of @c->value_str. This is excessive, hence we just + * ignore all those matches and accept everything from + * the kernel. People should really avoid those matches. + * If they're used in real-life some day, we will have + * to properly support multiple-matches here. + */ + break; + + case BUS_MATCH_ARG_NAMESPACE...BUS_MATCH_ARG_NAMESPACE_LAST: { + char buf[sizeof("arg")-1 + 2 + sizeof("-dot-prefix")]; + + xsprintf(buf, "arg%i-dot-prefix", c->type - BUS_MATCH_ARG_NAMESPACE); + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); + using_bloom = true; + break; + } + + case BUS_MATCH_DESTINATION: + /* + * Kernel only supports matching on destination IDs, but + * not on destination names. So just skip the + * destination name restriction and verify it in + * user-space on retrieval. + */ + r = bus_kernel_parse_unique_name(c->value_str, &dst_id); + if (r < 0) + return r; + else if (r > 0) + sz += ALIGN8(offsetof(struct kdbus_item, id) + sizeof(uint64_t)); + + /* if not a broadcast, it cannot be a name-change */ + if (r <= 0 || dst_id != KDBUS_DST_ID_BROADCAST) + matches_name_change = false; + + 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) + bus->bloom_size); + + m = alloca0_align(sz, 8); + m->size = sz; + m->cookie = cookie; + + 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 (dst_id != KDBUS_MATCH_ID_ANY) { + item->size = offsetof(struct kdbus_item, id) + sizeof(uint64_t); + item->type = KDBUS_ITEM_DST_ID; + item->id = dst_id; + item = KDBUS_ITEM_NEXT(item); + } + + if (using_bloom) { + item->size = offsetof(struct kdbus_item, data64) + bus->bloom_size; + item->type = KDBUS_ITEM_BLOOM_MASK; + memcpy(item->data64, bloom, bus->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; +} + +#define internal_match(bus, m) \ + ((bus)->hello_flags & KDBUS_HELLO_MONITOR \ + ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \ + : (m)) + +static int bus_add_match_internal_dbus1( + sd_bus *bus, + const char *match) { + + const char *e; + + assert(bus); + assert(match); + + e = internal_match(bus, match); + + return sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "AddMatch", + NULL, + NULL, + "s", + e); +} + +int bus_add_match_internal( + sd_bus *bus, + const char *match, + struct bus_match_component *components, + unsigned n_components, + uint64_t cookie) { + + assert(bus); + + if (!bus->bus_client) + return -EINVAL; + + if (bus->is_kernel) + return bus_add_match_internal_kernel(bus, components, n_components, cookie); + else + return bus_add_match_internal_dbus1(bus, match); +} + +int bus_remove_match_internal_kernel( + sd_bus *bus, + uint64_t cookie) { + + struct kdbus_cmd_match m = { + .size = offsetof(struct kdbus_cmd_match, items), + .cookie = cookie, + }; + int r; + + assert(bus); + + /* Monitor streams don't support matches, make this a NOP */ + if (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + + 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) { + + const char *e; + + assert(bus); + assert(match); + + e = internal_match(bus, match); + + return sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "RemoveMatch", + NULL, + NULL, + "s", + e); +} + +int bus_remove_match_internal( + sd_bus *bus, + const char *match, + uint64_t cookie) { + + assert(bus); + + if (!bus->bus_client) + return -EINVAL; + + if (bus->is_kernel) + return bus_remove_match_internal_kernel(bus, cookie); + else + return bus_remove_match_internal_dbus1(bus, match); +} + +_public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) { + _cleanup_(sd_bus_message_unrefp) 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_pid_changed(bus), -ECHILD); + assert_return(service_name_is_valid(name), -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (streq_ptr(name, bus->unique_name)) + return sd_id128_get_machine(machine); + + r = sd_bus_message_new_method_call( + bus, + &m, + name, + "/", + "org.freedesktop.DBus.Peer", + "GetMachineId"); + if (r < 0) + return r; + + r = sd_bus_message_set_auto_start(m, false); + 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/src/sd-bus/bus-control.h b/src/libsystemd/src/sd-bus/bus-control.h new file mode 100644 index 0000000000..229c95efb0 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-control.h @@ -0,0 +1,32 @@ +#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 . +***/ + +#include + +#include "bus-match.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, struct bus_match_component *components, unsigned n_components, uint64_t cookie); +int bus_remove_match_internal_kernel(sd_bus *bus, uint64_t cookie); + +int bus_get_name_creds_kdbus(sd_bus *bus, const char *name, uint64_t mask, bool allow_activator, sd_bus_creds **creds); diff --git a/src/libsystemd/src/sd-bus/bus-convenience.c b/src/libsystemd/src/sd-bus/bus-convenience.c new file mode 100644 index 0000000000..2d06bf541f --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-convenience.c @@ -0,0 +1,626 @@ +/*** + 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 . +***/ + +#include "bus-internal.h" +#include "bus-message.h" +#include "bus-signature.h" +#include "bus-type.h" +#include "bus-util.h" +#include "string-util.h" + +_public_ int sd_bus_emit_signal( + sd_bus *bus, + const char *path, + const char *interface, + const char *member, + const char *types, ...) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + r = sd_bus_message_new_signal(bus, &m, path, interface, member); + 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_async( + sd_bus *bus, + sd_bus_slot **slot, + const char *destination, + const char *path, + const char *interface, + const char *member, + sd_bus_message_handler_t callback, + void *userdata, + const char *types, ...) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member); + 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_async(bus, slot, m, callback, userdata, 0); +} + +_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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + bus_assert_return(bus, -EINVAL, error); + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member); + if (r < 0) + goto fail; + + if (!isempty(types)) { + va_list ap; + + va_start(ap, types); + r = bus_message_append_ap(m, types, ap); + va_end(ap); + if (r < 0) + goto fail; + } + + return sd_bus_call(bus, m, 0, error, reply); + +fail: + return sd_bus_error_set_errno(error, r); +} + +_public_ int sd_bus_reply_method_return( + sd_bus_message *call, + const char *types, ...) { + + _cleanup_(sd_bus_message_unrefp) 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, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + 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_(sd_bus_message_unrefp) 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, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) + return 0; + + r = sd_bus_message_new_method_error(call, &m, e); + 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_(sd_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, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + 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_(sd_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, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + 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_(sd_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, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) + return 0; + + va_start(ap, format); + sd_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; + + bus_assert_return(bus, -EINVAL, error); + bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); + bus_assert_return(member_name_is_valid(member), -EINVAL, error); + bus_assert_return(reply, -EINVAL, error); + bus_assert_return(signature_is_single(type, false), -EINVAL, error); + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + 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); + goto fail; + } + + *reply = rep; + return 0; + +fail: + return sd_bus_error_set_errno(error, r); +} + +_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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + bus_assert_return(bus, -EINVAL, error); + bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); + bus_assert_return(member_name_is_valid(member), -EINVAL, error); + bus_assert_return(bus_type_is_trivial(type), -EINVAL, error); + bus_assert_return(ptr, -EINVAL, error); + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + 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) + goto fail; + + r = sd_bus_message_read_basic(reply, type, ptr); + if (r < 0) + goto fail; + + return 0; + +fail: + return sd_bus_error_set_errno(error, r); +} + +_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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *s; + char *n; + int r; + + bus_assert_return(bus, -EINVAL, error); + bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); + bus_assert_return(member_name_is_valid(member), -EINVAL, error); + bus_assert_return(ret, -EINVAL, error); + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + 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) + goto fail; + + r = sd_bus_message_read_basic(reply, 's', &s); + if (r < 0) + goto fail; + + n = strdup(s); + if (!n) { + r = -ENOMEM; + goto fail; + } + + *ret = n; + return 0; + +fail: + return sd_bus_error_set_errno(error, r); +} + +_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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + bus_assert_return(bus, -EINVAL, error); + bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); + bus_assert_return(member_name_is_valid(member), -EINVAL, error); + bus_assert_return(ret, -EINVAL, error); + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + 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) + goto fail; + + r = sd_bus_message_read_strv(reply, ret); + if (r < 0) + goto fail; + + return 0; + +fail: + return sd_bus_error_set_errno(error, r); +} + +_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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + va_list ap; + int r; + + bus_assert_return(bus, -EINVAL, error); + bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); + bus_assert_return(member_name_is_valid(member), -EINVAL, error); + bus_assert_return(signature_is_single(type, false), -EINVAL, error); + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set"); + if (r < 0) + goto fail; + + r = sd_bus_message_append(m, "ss", strempty(interface), member); + if (r < 0) + goto fail; + + r = sd_bus_message_open_container(m, 'v', type); + if (r < 0) + goto fail; + + va_start(ap, type); + r = bus_message_append_ap(m, type, ap); + va_end(ap); + if (r < 0) + goto fail; + + r = sd_bus_message_close_container(m); + if (r < 0) + goto fail; + + return sd_bus_call(bus, m, 0, error, NULL); + +fail: + return sd_bus_error_set_errno(error, r); +} + +_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, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + 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) + /* There's a sender, but the creds are + * missing. This means we are talking via + * dbus1, or are getting a message that was + * sent to us via kdbus, but was converted + * from a dbus1 message by the bus-proxy and + * thus also lacks the creds. */ + return sd_bus_get_name_creds(call->bus, call->sender, mask, creds); + else + /* There's no sender, hence we are on a dbus1 + * direct connection. For direct connections + * the credentials of the AF_UNIX peer matter, + * which may be queried via + * sd_bus_get_owner_creds(). */ + return sd_bus_get_owner_creds(call->bus, mask, creds); + } + + return bus_creds_extend_by_pid(c, mask, creds); +} + +_public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + uid_t our_uid; + bool know_caps = false; + int r; + + assert_return(call, -EINVAL); + assert_return(call->sealed, -EPERM); + assert_return(call->bus, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + if (capability >= 0) { + + r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds); + if (r < 0) + return r; + + /* We cannot use augmented caps for authorization, + * since then data is acquired raceful from + * /proc. This can never actually happen, but let's + * better be safe than sorry, and do an extra check + * here. */ + assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EFFECTIVE_CAPS) == 0, -EPERM); + + /* Note that not even on kdbus we might have the caps + * field, due to faked identities, or namespace + * translation issues. */ + r = sd_bus_creds_has_effective_cap(creds, capability); + if (r > 0) + return 1; + if (r == 0) + know_caps = true; + } else { + r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + } + + /* Now, check the UID, but only if the capability check wasn't + * sufficient */ + our_uid = getuid(); + if (our_uid != 0 || !know_caps || capability < 0) { + uid_t sender_uid; + + /* We cannot use augmented uid/euid for authorization, + * since then data is acquired raceful from + * /proc. This can never actually happen, but let's + * better be safe than sorry, and do an extra check + * here. */ + assert_return((sd_bus_creds_get_augmented_mask(creds) & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID)) == 0, -EPERM); + + /* Try to use the EUID, if we have it. */ + r = sd_bus_creds_get_euid(creds, &sender_uid); + if (r < 0) + r = sd_bus_creds_get_uid(creds, &sender_uid); + + if (r >= 0) { + /* Sender has same UID as us, then let's grant access */ + if (sender_uid == our_uid) + return 1; + + /* Sender is root, we are not root. */ + if (our_uid != 0 && sender_uid == 0) + return 1; + } + } + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/bus-creds.c b/src/libsystemd/src/sd-bus/bus-creds.c new file mode 100644 index 0000000000..c4f693dee9 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-creds.c @@ -0,0 +1,1349 @@ +/*** + 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 . +***/ + +#include +#include + +#include "alloc-util.h" +#include "audit-util.h" +#include "bus-creds.h" +#include "bus-label.h" +#include "bus-message.h" +#include "bus-util.h" +#include "capability-util.h" +#include "cgroup-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "formats-util.h" +#include "hexdecoct.h" +#include "parse-util.h" +#include "process-util.h" +#include "string-util.h" +#include "strv.h" +#include "terminal-util.h" +#include "user-util.h" +#include "util.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); + free(c->user_slice); + free(c->unescaped_description); + free(c->supplementary_gids); + free(c->tty); + + free(c->well_known_names); /* note that this is an strv, but + * we only free the array, not the + * strings the array points to. The + * full strv we only free if + * c->allocated is set, see + * below. */ + + strv_free(c->cmdline_array); +} + +_public_ sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c) { + + if (!c) + return 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) { + 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->description); + + c->supplementary_gids = mfree(c->supplementary_gids); + + c->well_known_names = strv_free(c->well_known_names); + + bus_creds_done(c); + + 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; +} + +_public_ uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c) { + assert_return(c, 0); + + return c->augmented; +} + +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(sd_bus_creds **ret, pid_t pid, uint64_t mask) { + sd_bus_creds *c; + int r; + + assert_return(pid >= 0, -EINVAL); + assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); + assert_return(ret, -EINVAL); + + if (pid == 0) + pid = getpid(); + + c = bus_creds_new(); + if (!c) + return -ENOMEM; + + r = bus_creds_add_more(c, mask | SD_BUS_CREDS_AUGMENT, 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 (!pid_is_alive(pid)) { + 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_euid(sd_bus_creds *c, uid_t *euid) { + assert_return(c, -EINVAL); + assert_return(euid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_EUID)) + return -ENODATA; + + *euid = c->euid; + return 0; +} + +_public_ int sd_bus_creds_get_suid(sd_bus_creds *c, uid_t *suid) { + assert_return(c, -EINVAL); + assert_return(suid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_SUID)) + return -ENODATA; + + *suid = c->suid; + return 0; +} + + +_public_ int sd_bus_creds_get_fsuid(sd_bus_creds *c, uid_t *fsuid) { + assert_return(c, -EINVAL); + assert_return(fsuid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_FSUID)) + return -ENODATA; + + *fsuid = c->fsuid; + 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_GID)) + return -ENODATA; + + *gid = c->gid; + return 0; +} + +_public_ int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) { + assert_return(c, -EINVAL); + assert_return(egid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_EGID)) + return -ENODATA; + + *egid = c->egid; + return 0; +} + +_public_ int sd_bus_creds_get_sgid(sd_bus_creds *c, gid_t *sgid) { + assert_return(c, -EINVAL); + assert_return(sgid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_SGID)) + return -ENODATA; + + *sgid = c->sgid; + return 0; +} + +_public_ int sd_bus_creds_get_fsgid(sd_bus_creds *c, gid_t *fsgid) { + assert_return(c, -EINVAL); + assert_return(fsgid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_FSGID)) + return -ENODATA; + + *fsgid = c->fsgid; + return 0; +} + +_public_ int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) { + assert_return(c, -EINVAL); + assert_return(gids, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS)) + return -ENODATA; + + *gids = c->supplementary_gids; + return (int) c->n_supplementary_gids; +} + +_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_ppid(sd_bus_creds *c, pid_t *ppid) { + assert_return(c, -EINVAL); + assert_return(ppid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_PPID)) + return -ENODATA; + + /* PID 1 has no parent process. Let's distinguish the case of + * not knowing and not having a parent process by the returned + * error code. */ + if (c->ppid == 0) + return -ENXIO; + + *ppid = c->ppid; + 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_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; + + if (!c->exe) + return -ENXIO; + + *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_user_slice(sd_bus_creds *c, const char **ret) { + int r; + + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_USER_SLICE)) + return -ENODATA; + + assert(c->cgroup); + + if (!c->user_slice) { + const char *shifted; + + r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); + if (r < 0) + return r; + + r = cg_path_get_user_slice(shifted, (char**) &c->user_slice); + if (r < 0) + return r; + } + + *ret = c->user_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; + + if (!c->cmdline) + return -ENXIO; + + 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; + + if (c->audit_session_id == AUDIT_SESSION_INVALID) + return -ENXIO; + + *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; + + if (c->audit_login_uid == UID_INVALID) + return -ENXIO; + + *uid = c->audit_login_uid; + return 0; +} + +_public_ int sd_bus_creds_get_tty(sd_bus_creds *c, const char **ret) { + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_TTY)) + return -ENODATA; + + if (!c->tty) + return -ENXIO; + + *ret = c->tty; + 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; + + /* As a special hack we return the bus driver as well-known + * names list when this is requested. */ + if (c->well_known_names_driver) { + static const char* const wkn[] = { + "org.freedesktop.DBus", + NULL + }; + + *well_known_names = (char**) wkn; + return 0; + } + + if (c->well_known_names_local) { + static const char* const wkn[] = { + "org.freedesktop.DBus.Local", + NULL + }; + + *well_known_names = (char**) wkn; + return 0; + } + + *well_known_names = c->well_known_names; + return 0; +} + +_public_ int sd_bus_creds_get_description(sd_bus_creds *c, const char **ret) { + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_DESCRIPTION)) + return -ENODATA; + + assert(c->description); + + if (!c->unescaped_description) { + c->unescaped_description = bus_label_unescape(c->description); + if (!c->unescaped_description) + return -ENOMEM; + } + + *ret = c->unescaped_description; + return 0; +} + +static int has_cap(sd_bus_creds *c, unsigned offset, int capability) { + size_t sz; + + assert(c); + assert(capability >= 0); + assert(c->capability); + + if ((unsigned) capability > cap_last_cap()) + return 0; + + sz = DIV_ROUND_UP(cap_last_cap(), 32U); + + return !!(c->capability[offset * sz + CAP_TO_INDEX(capability)] & CAP_TO_MASK(capability)); +} + +_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, max; + unsigned i, j; + + assert(c); + assert(p); + + max = DIV_ROUND_UP(cap_last_cap(), 32U); + p += strspn(p, WHITESPACE); + + sz = strlen(p); + if (sz % 8 != 0) + return -EINVAL; + + sz /= 8; + if (sz > max) + return -EINVAL; + + if (!c->capability) { + c->capability = new0(uint32_t, max * 4); + if (!c->capability) + return -ENOMEM; + } + + for (i = 0; i < sz; i ++) { + uint32_t v = 0; + + for (j = 0; j < 8; ++j) { + int t; + + t = unhexchar(*p++); + if (t < 0) + return -EINVAL; + + v = (v << 4) | t; + } + + c->capability[offset * max + (sz - i - 1)] = v; + } + + 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); + + if (!(mask & SD_BUS_CREDS_AUGMENT)) + return 0; + + /* Try to retrieve PID from creds if it wasn't passed to us */ + if (pid > 0) { + c->pid = pid; + c->mask |= SD_BUS_CREDS_PID; + } else if (c->mask & SD_BUS_CREDS_PID) + pid = c->pid; + else + /* Without pid we cannot do much... */ + return 0; + + /* Try to retrieve TID from creds if it wasn't passed to us */ + if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID)) + tid = c->tid; + + /* Calculate what we shall and can add */ + missing = mask & ~(c->mask|SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_DESCRIPTION|SD_BUS_CREDS_AUGMENT); + if (missing == 0) + return 0; + + if (tid > 0) { + c->tid = tid; + c->mask |= SD_BUS_CREDS_TID; + } + + if (missing & (SD_BUS_CREDS_PPID | + SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_SUID | SD_BUS_CREDS_FSUID | + SD_BUS_CREDS_GID | SD_BUS_CREDS_EGID | SD_BUS_CREDS_SGID | SD_BUS_CREDS_FSGID | + SD_BUS_CREDS_SUPPLEMENTARY_GIDS | + 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; + const char *p; + + p = procfs_file_alloca(pid, "status"); + + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) + return -ESRCH; + else if (errno != EPERM && errno != EACCES) + return -errno; + } else { + char line[LINE_MAX]; + + FOREACH_LINE(line, f, return -errno) { + truncate_nl(line); + + if (missing & SD_BUS_CREDS_PPID) { + p = startswith(line, "PPid:"); + if (p) { + p += strspn(p, WHITESPACE); + + /* Explicitly check for PPID 0 (which is the case for PID 1) */ + if (!streq(p, "0")) { + r = parse_pid(p, &c->ppid); + if (r < 0) + return r; + + } else + c->ppid = 0; + + c->mask |= SD_BUS_CREDS_PPID; + continue; + } + } + + if (missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID)) { + p = startswith(line, "Uid:"); + if (p) { + unsigned long uid, euid, suid, fsuid; + + p += strspn(p, WHITESPACE); + if (sscanf(p, "%lu %lu %lu %lu", &uid, &euid, &suid, &fsuid) != 4) + return -EIO; + + if (missing & SD_BUS_CREDS_UID) + c->uid = (uid_t) uid; + if (missing & SD_BUS_CREDS_EUID) + c->euid = (uid_t) euid; + if (missing & SD_BUS_CREDS_SUID) + c->suid = (uid_t) suid; + if (missing & SD_BUS_CREDS_FSUID) + c->fsuid = (uid_t) fsuid; + + c->mask |= missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID); + continue; + } + } + + if (missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) { + p = startswith(line, "Gid:"); + if (p) { + unsigned long gid, egid, sgid, fsgid; + + p += strspn(p, WHITESPACE); + if (sscanf(p, "%lu %lu %lu %lu", &gid, &egid, &sgid, &fsgid) != 4) + return -EIO; + + if (missing & SD_BUS_CREDS_GID) + c->gid = (gid_t) gid; + if (missing & SD_BUS_CREDS_EGID) + c->egid = (gid_t) egid; + if (missing & SD_BUS_CREDS_SGID) + c->sgid = (gid_t) sgid; + if (missing & SD_BUS_CREDS_FSGID) + c->fsgid = (gid_t) fsgid; + + c->mask |= missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID); + continue; + } + } + + if (missing & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + p = startswith(line, "Groups:"); + if (p) { + size_t allocated = 0; + + for (;;) { + unsigned long g; + int n = 0; + + p += strspn(p, WHITESPACE); + if (*p == 0) + break; + + if (sscanf(p, "%lu%n", &g, &n) != 1) + return -EIO; + + if (!GREEDY_REALLOC(c->supplementary_gids, allocated, c->n_supplementary_gids+1)) + return -ENOMEM; + + c->supplementary_gids[c->n_supplementary_gids++] = (gid_t) g; + p += n; + } + + c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + 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_SELINUX_CONTEXT) { + const char *p; + + p = procfs_file_alloca(pid, "attr/current"); + r = read_one_line_file(p, &c->label); + if (r < 0) { + if (r != -ENOENT && r != -EINVAL && r != -EPERM && r != -EACCES) + return r; + } else + c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + } + + if (missing & SD_BUS_CREDS_COMM) { + r = get_process_comm(pid, &c->comm); + if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } else + c->mask |= SD_BUS_CREDS_COMM; + } + + if (missing & SD_BUS_CREDS_EXE) { + r = get_process_exe(pid, &c->exe); + if (r == -ESRCH) { + /* Unfortunately we cannot really distinguish + * the case here where the process does not + * exist, and /proc/$PID/exe being unreadable + * because $PID is a kernel thread. Hence, + * assume it is a kernel thread, and rely on + * that this case is caught with a later + * call. */ + c->exe = NULL; + c->mask |= SD_BUS_CREDS_EXE; + } else if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } else + 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 == -ENOENT) + return -ESRCH; + if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } else { + if (c->cmdline_size == 0) + c->cmdline = mfree(c->cmdline); + + c->mask |= SD_BUS_CREDS_CMDLINE; + } + } + + if (tid > 0 && (missing & SD_BUS_CREDS_TID_COMM)) { + _cleanup_free_ char *p = NULL; + + if (asprintf(&p, "/proc/"PID_FMT"/task/"PID_FMT"/comm", pid, tid) < 0) + return -ENOMEM; + + r = read_one_line_file(p, &c->tid_comm); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } else + 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_USER_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) { + + if (!c->cgroup) { + r = cg_pid_get_path(NULL, pid, &c->cgroup); + if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } + } + + if (!c->cgroup_root) { + r = cg_get_root_path(&c->cgroup_root); + if (r < 0) + return r; + } + + if (c->cgroup) + c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_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 == -ENODATA) { + /* ENODATA means: no audit session id assigned */ + c->audit_session_id = AUDIT_SESSION_INVALID; + c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; + } else if (r < 0) { + if (r != -EOPNOTSUPP && r != -ENOENT && r != -EPERM && r != -EACCES) + return r; + } else + 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 == -ENODATA) { + /* ENODATA means: no audit login uid assigned */ + c->audit_login_uid = UID_INVALID; + c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; + } else if (r < 0) { + if (r != -EOPNOTSUPP && r != -ENOENT && r != -EPERM && r != -EACCES) + return r; + } else + c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; + } + + if (missing & SD_BUS_CREDS_TTY) { + r = get_ctty(pid, NULL, &c->tty); + if (r == -ENXIO) { + /* ENXIO means: process has no controlling TTY */ + c->tty = NULL; + c->mask |= SD_BUS_CREDS_TTY; + } else if (r < 0) { + if (r != -EPERM && r != -EACCES && r != -ENOENT) + return r; + } else + c->mask |= SD_BUS_CREDS_TTY; + } + + /* In case only the exe path was to be read we cannot + * distinguish the case where the exe path was unreadable + * because the process was a kernel thread, or when the + * process didn't exist at all. Hence, let's do a final check, + * to be sure. */ + if (!pid_is_alive(pid)) + return -ESRCH; + + if (tid > 0 && tid != pid && !pid_is_unwaited(tid)) + return -ESRCH; + + c->augmented = missing & c->mask; + + return 0; +} + +int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *n = NULL; + int r; + + assert(c); + assert(ret); + + if ((mask & ~c->mask) == 0 || (!(mask & SD_BUS_CREDS_AUGMENT))) { + /* There's already all data we need, or augmentation + * wasn't turned on. */ + + *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_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_PPID) { + n->ppid = c->ppid; + n->mask |= SD_BUS_CREDS_PPID; + } + + 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_EUID) { + n->euid = c->euid; + n->mask |= SD_BUS_CREDS_EUID; + } + + if (c->mask & mask & SD_BUS_CREDS_SUID) { + n->suid = c->suid; + n->mask |= SD_BUS_CREDS_SUID; + } + + if (c->mask & mask & SD_BUS_CREDS_FSUID) { + n->fsuid = c->fsuid; + n->mask |= SD_BUS_CREDS_FSUID; + } + + 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_EGID) { + n->egid = c->egid; + n->mask |= SD_BUS_CREDS_EGID; + } + + if (c->mask & mask & SD_BUS_CREDS_SGID) { + n->sgid = c->sgid; + n->mask |= SD_BUS_CREDS_SGID; + } + + if (c->mask & mask & SD_BUS_CREDS_FSGID) { + n->fsgid = c->fsgid; + n->mask |= SD_BUS_CREDS_FSGID; + } + + if (c->mask & mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + if (c->supplementary_gids) { + n->supplementary_gids = newdup(gid_t, c->supplementary_gids, c->n_supplementary_gids); + if (!n->supplementary_gids) + return -ENOMEM; + n->n_supplementary_gids = c->n_supplementary_gids; + } else { + n->supplementary_gids = NULL; + n->n_supplementary_gids = 0; + } + + n->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + } + + if (c->mask & mask & SD_BUS_CREDS_COMM) { + assert(c->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) { + assert(c->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) { + if (c->exe) { + n->exe = strdup(c->exe); + if (!n->exe) + return -ENOMEM; + } else + n->exe = NULL; + + n->mask |= SD_BUS_CREDS_EXE; + } + + if (c->mask & mask & SD_BUS_CREDS_CMDLINE) { + if (c->cmdline) { + n->cmdline = memdup(c->cmdline, c->cmdline_size); + if (!n->cmdline) + return -ENOMEM; + + n->cmdline_size = c->cmdline_size; + } else { + n->cmdline = NULL; + n->cmdline_size = 0; + } + + 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_USER_SLICE|SD_BUS_CREDS_OWNER_UID)) { + assert(c->cgroup); + + 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_USER_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)) { + assert(c->capability); + + n->capability = memdup(c->capability, DIV_ROUND_UP(cap_last_cap(), 32U) * 4 * 4); + if (!n->capability) + return -ENOMEM; + + 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_SELINUX_CONTEXT) { + assert(c->label); + + n->label = strdup(c->label); + if (!n->label) + return -ENOMEM; + n->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + } + + 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_TTY) { + if (c->tty) { + n->tty = strdup(c->tty); + if (!n->tty) + return -ENOMEM; + } else + n->tty = NULL; + n->mask |= SD_BUS_CREDS_TTY; + } + + if (c->mask & mask & SD_BUS_CREDS_UNIQUE_NAME) { + assert(c->unique_name); + + n->unique_name = strdup(c->unique_name); + if (!n->unique_name) + return -ENOMEM; + n->mask |= SD_BUS_CREDS_UNIQUE_NAME; + } + + if (c->mask & mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { + if (strv_isempty(c->well_known_names)) + n->well_known_names = NULL; + else { + n->well_known_names = strv_copy(c->well_known_names); + if (!n->well_known_names) + return -ENOMEM; + } + n->well_known_names_driver = c->well_known_names_driver; + n->well_known_names_local = c->well_known_names_local; + n->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; + } + + if (c->mask & mask & SD_BUS_CREDS_DESCRIPTION) { + assert(c->description); + n->description = strdup(c->description); + if (!n->description) + return -ENOMEM; + n->mask |= SD_BUS_CREDS_DESCRIPTION; + } + + n->augmented = c->augmented & n->mask; + + /* Get more data */ + + r = bus_creds_add_more(n, mask, 0, 0); + if (r < 0) + return r; + + *ret = n; + n = NULL; + return 0; +} diff --git a/src/libsystemd/src/sd-bus/bus-creds.h b/src/libsystemd/src/sd-bus/bus-creds.h new file mode 100644 index 0000000000..3e2311f91d --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-creds.h @@ -0,0 +1,90 @@ +#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 . +***/ + +#include + +#include + +struct sd_bus_creds { + bool allocated; + unsigned n_ref; + + uint64_t mask; + uint64_t augmented; + + uid_t uid; + uid_t euid; + uid_t suid; + uid_t fsuid; + gid_t gid; + gid_t egid; + gid_t sgid; + gid_t fsgid; + + gid_t *supplementary_gids; + unsigned n_supplementary_gids; + + pid_t ppid; + pid_t pid; + 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; + char *user_slice; + + char *tty; + + uint32_t *capability; + + uint32_t audit_session_id; + uid_t audit_login_uid; + + char *label; + + char *unique_name; + + char **well_known_names; + bool well_known_names_driver:1; + bool well_known_names_local:1; + + char *cgroup_root; + + char *description, *unescaped_description; +}; + +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/src/sd-bus/bus-dump.c b/src/libsystemd/src/sd-bus/bus-dump.c new file mode 100644 index 0000000000..21a6b20a11 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-dump.c @@ -0,0 +1,602 @@ +/*** + 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 . +***/ + +#include "alloc-util.h" +#include "bus-dump.h" +#include "bus-internal.h" +#include "bus-message.h" +#include "bus-type.h" +#include "cap-list.h" +#include "capability-util.h" +#include "fileio.h" +#include "formats-util.h" +#include "locale-util.h" +#include "macro.h" +#include "string-util.h" +#include "strv.h" +#include "terminal-util.h" +#include "util.h" + +static char *indent(unsigned level, unsigned flags) { + char *p; + unsigned n, i = 0; + + n = 0; + + if (flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY && level > 0) + level -= 1; + + if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) + n += 2; + + p = new(char, n + level*8 + 1); + if (!p) + return NULL; + + if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) { + p[i++] = ' '; + p[i++] = ' '; + } + + memset(p + i, ' ', level*8); + p[i + level*8] = 0; + + return p; +} + +int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) { + unsigned level = 1; + int r; + + assert(m); + + if (!f) + f = stdout; + + if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) { + fprintf(f, + "%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u Priority=%"PRIi64, + 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() : "", special_glyph(TRIANGULAR_BULLET), ansi_normal(), + ansi_highlight(), bus_message_type_to_string(m->header->type), ansi_normal(), + m->header->endian, + m->header->flags, + m->header->version, + m->priority); + + /* 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=%" PRIu64, BUS_MESSAGE_COOKIE(m)); + + if (m->reply_cookie != 0) + fprintf(f, " ReplyCookie=%" PRIu64, m->reply_cookie); + + fputs("\n", f); + + if (m->sender) + fprintf(f, " Sender=%s%s%s", ansi_highlight(), m->sender, ansi_normal()); + if (m->destination) + fprintf(f, " Destination=%s%s%s", ansi_highlight(), m->destination, ansi_normal()); + if (m->path) + fprintf(f, " Path=%s%s%s", ansi_highlight(), m->path, ansi_normal()); + if (m->interface) + fprintf(f, " Interface=%s%s%s", ansi_highlight(), m->interface, ansi_normal()); + if (m->member) + fprintf(f, " Member=%s%s%s", ansi_highlight(), m->member, ansi_normal()); + + 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_normal(), + ansi_highlight_red(), strna(m->error.message), ansi_normal()); + + if (m->monotonic != 0) + fprintf(f, " Monotonic="USEC_FMT, m->monotonic); + if (m->realtime != 0) + fprintf(f, " Realtime="USEC_FMT, m->realtime); + if (m->seqnum != 0) + fprintf(f, " SequenceNumber=%"PRIu64, m->seqnum); + + if (m->monotonic != 0 || m->realtime != 0 || m->seqnum != 0) + fputs("\n", f); + + bus_creds_dump(&m->creds, f, true); + } + + r = sd_bus_message_rewind(m, !(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)); + if (r < 0) + return log_error_errno(r, "Failed to rewind: %m"); + + if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) { + _cleanup_free_ char *prefix = NULL; + + prefix = indent(0, flags); + if (!prefix) + return log_oom(); + + fprintf(f, "%sMESSAGE \"%s\" {\n", prefix, 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) + return log_error_errno(r, "Failed to peek type: %m"); + + if (r == 0) { + if (level <= 1) + break; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return log_error_errno(r, "Failed to exit container: %m"); + + level--; + + prefix = indent(level, flags); + if (!prefix) + return log_oom(); + + fprintf(f, "%s};\n", prefix); + continue; + } + + prefix = indent(level, flags); + if (!prefix) + return log_oom(); + + if (bus_type_is_container(type) > 0) { + r = sd_bus_message_enter_container(m, type, contents); + if (r < 0) + return log_error_errno(r, "Failed to enter container: %m"); + + 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) + return log_error_errno(r, "Failed to get basic: %m"); + + assert(r > 0); + + switch (type) { + + case SD_BUS_TYPE_BYTE: + fprintf(f, "%sBYTE %s%u%s;\n", prefix, ansi_highlight(), basic.u8, ansi_normal()); + break; + + case SD_BUS_TYPE_BOOLEAN: + fprintf(f, "%sBOOLEAN %s%s%s;\n", prefix, ansi_highlight(), true_false(basic.i), ansi_normal()); + break; + + case SD_BUS_TYPE_INT16: + fprintf(f, "%sINT16 %s%i%s;\n", prefix, ansi_highlight(), basic.s16, ansi_normal()); + break; + + case SD_BUS_TYPE_UINT16: + fprintf(f, "%sUINT16 %s%u%s;\n", prefix, ansi_highlight(), basic.u16, ansi_normal()); + break; + + case SD_BUS_TYPE_INT32: + fprintf(f, "%sINT32 %s%i%s;\n", prefix, ansi_highlight(), basic.s32, ansi_normal()); + break; + + case SD_BUS_TYPE_UINT32: + fprintf(f, "%sUINT32 %s%u%s;\n", prefix, ansi_highlight(), basic.u32, ansi_normal()); + break; + + case SD_BUS_TYPE_INT64: + fprintf(f, "%sINT64 %s%"PRIi64"%s;\n", prefix, ansi_highlight(), basic.s64, ansi_normal()); + break; + + case SD_BUS_TYPE_UINT64: + fprintf(f, "%sUINT64 %s%"PRIu64"%s;\n", prefix, ansi_highlight(), basic.u64, ansi_normal()); + break; + + case SD_BUS_TYPE_DOUBLE: + fprintf(f, "%sDOUBLE %s%g%s;\n", prefix, ansi_highlight(), basic.d64, ansi_normal()); + break; + + case SD_BUS_TYPE_STRING: + fprintf(f, "%sSTRING \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal()); + break; + + case SD_BUS_TYPE_OBJECT_PATH: + fprintf(f, "%sOBJECT_PATH \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal()); + break; + + case SD_BUS_TYPE_SIGNATURE: + fprintf(f, "%sSIGNATURE \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal()); + break; + + case SD_BUS_TYPE_UNIX_FD: + fprintf(f, "%sUNIX_FD %s%i%s;\n", prefix, ansi_highlight(), basic.i, ansi_normal()); + break; + + default: + assert_not_reached("Unknown basic type."); + } + } + + if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) { + _cleanup_free_ char *prefix = NULL; + + prefix = indent(0, flags); + if (!prefix) + return log_oom(); + + fprintf(f, "%s};\n\n", prefix); + } + + return 0; +} + +static void dump_capabilities( + sd_bus_creds *c, + FILE *f, + const char *name, + bool terse, + 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%s=%s", terse ? " " : "", name, terse ? "" : ansi_highlight()); + last_cap = cap_last_cap(); + + for (;;) { + if (r > 0) { + + if (n > 0) + fputc(' ', f); + if (n % 4 == 3) + fprintf(f, terse ? "\n " : "\n "); + + fprintf(f, "%s", strna(capability_to_name(i))); + n++; + } + + i++; + + if (i > last_cap) + break; + + r = has(c, i); + } + + fputs("\n", f); + + if (!terse) + fputs(ansi_normal(), f); +} + +int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { + uid_t owner, audit_loginuid; + uint32_t audit_sessionid; + char **cmdline = NULL, **well_known = NULL; + const char *prefix, *color, *suffix, *s; + int r, q, v, w, z; + + assert(c); + + if (!f) + f = stdout; + + if (terse) { + prefix = " "; + suffix = ""; + color = ""; + } else { + const char *off; + + prefix = ""; + color = ansi_highlight(); + + off = ansi_normal(); + suffix = strjoina(off, "\n"); + } + + if (c->mask & SD_BUS_CREDS_PID) + fprintf(f, "%sPID=%s"PID_FMT"%s", prefix, color, c->pid, suffix); + if (c->mask & SD_BUS_CREDS_TID) + fprintf(f, "%sTID=%s"PID_FMT"%s", prefix, color, c->tid, suffix); + if (c->mask & SD_BUS_CREDS_PPID) { + if (c->ppid == 0) + fprintf(f, "%sPPID=%sn/a%s", prefix, color, suffix); + else + fprintf(f, "%sPPID=%s"PID_FMT"%s", prefix, color, c->ppid, suffix); + } + if (c->mask & SD_BUS_CREDS_TTY) + fprintf(f, "%sTTY=%s%s%s", prefix, color, strna(c->tty), suffix); + + if (terse && ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID|SD_BUS_CREDS_TTY)))) + fputs("\n", f); + + if (c->mask & SD_BUS_CREDS_UID) + fprintf(f, "%sUID=%s"UID_FMT"%s", prefix, color, c->uid, suffix); + if (c->mask & SD_BUS_CREDS_EUID) + fprintf(f, "%sEUID=%s"UID_FMT"%s", prefix, color, c->euid, suffix); + if (c->mask & SD_BUS_CREDS_SUID) + fprintf(f, "%sSUID=%s"UID_FMT"%s", prefix, color, c->suid, suffix); + if (c->mask & SD_BUS_CREDS_FSUID) + fprintf(f, "%sFSUID=%s"UID_FMT"%s", prefix, color, c->fsuid, suffix); + r = sd_bus_creds_get_owner_uid(c, &owner); + if (r >= 0) + fprintf(f, "%sOwnerUID=%s"UID_FMT"%s", prefix, color, owner, suffix); + if (c->mask & SD_BUS_CREDS_GID) + fprintf(f, "%sGID=%s"GID_FMT"%s", prefix, color, c->gid, suffix); + if (c->mask & SD_BUS_CREDS_EGID) + fprintf(f, "%sEGID=%s"GID_FMT"%s", prefix, color, c->egid, suffix); + if (c->mask & SD_BUS_CREDS_SGID) + fprintf(f, "%sSGID=%s"GID_FMT"%s", prefix, color, c->sgid, suffix); + if (c->mask & SD_BUS_CREDS_FSGID) + fprintf(f, "%sFSGID=%s"GID_FMT"%s", prefix, color, c->fsgid, suffix); + + if (c->mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + unsigned i; + + fprintf(f, "%sSupplementaryGIDs=%s", prefix, color); + for (i = 0; i < c->n_supplementary_gids; i++) + fprintf(f, "%s" GID_FMT, i > 0 ? " " : "", c->supplementary_gids[i]); + fprintf(f, "%s", suffix); + } + + if (terse && ((c->mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS)) || r >= 0)) + fputs("\n", f); + + if (c->mask & SD_BUS_CREDS_COMM) + fprintf(f, "%sComm=%s%s%s", prefix, color, c->comm, suffix); + if (c->mask & SD_BUS_CREDS_TID_COMM) + fprintf(f, "%sTIDComm=%s%s%s", prefix, color, c->tid_comm, suffix); + if (c->mask & SD_BUS_CREDS_EXE) + fprintf(f, "%sExe=%s%s%s", prefix, color, strna(c->exe), suffix); + + if (terse && (c->mask & (SD_BUS_CREDS_EXE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM))) + fputs("\n", f); + + r = sd_bus_creds_get_cmdline(c, &cmdline); + if (r >= 0) { + char **i; + + fprintf(f, "%sCommandLine=%s", prefix, color); + STRV_FOREACH(i, cmdline) { + if (i != cmdline) + fputc(' ', f); + + fputs(*i, f); + } + + fprintf(f, "%s", suffix); + } else if (r != -ENODATA) + fprintf(f, "%sCommandLine=%sn/a%s", prefix, color, suffix); + + if (c->mask & SD_BUS_CREDS_SELINUX_CONTEXT) + fprintf(f, "%sLabel=%s%s%s", prefix, color, c->label, suffix); + if (c->mask & SD_BUS_CREDS_DESCRIPTION) + fprintf(f, "%sDescription=%s%s%s", prefix, color, c->description, suffix); + + if (terse && (c->mask & (SD_BUS_CREDS_SELINUX_CONTEXT|SD_BUS_CREDS_DESCRIPTION))) + fputs("\n", f); + + if (c->mask & SD_BUS_CREDS_CGROUP) + fprintf(f, "%sCGroup=%s%s%s", prefix, color, c->cgroup, suffix); + s = NULL; + r = sd_bus_creds_get_unit(c, &s); + if (r != -ENODATA) + fprintf(f, "%sUnit=%s%s%s", prefix, color, strna(s), suffix); + s = NULL; + v = sd_bus_creds_get_slice(c, &s); + if (v != -ENODATA) + fprintf(f, "%sSlice=%s%s%s", prefix, color, strna(s), suffix); + s = NULL; + q = sd_bus_creds_get_user_unit(c, &s); + if (q != -ENODATA) + fprintf(f, "%sUserUnit=%s%s%s", prefix, color, strna(s), suffix); + s = NULL; + w = sd_bus_creds_get_user_slice(c, &s); + if (w != -ENODATA) + fprintf(f, "%sUserSlice=%s%s%s", prefix, color, strna(s), suffix); + s = NULL; + z = sd_bus_creds_get_session(c, &s); + if (z != -ENODATA) + fprintf(f, "%sSession=%s%s%s", prefix, color, strna(s), suffix); + + if (terse && ((c->mask & SD_BUS_CREDS_CGROUP) || r != -ENODATA || q != -ENODATA || v != -ENODATA || w != -ENODATA || z != -ENODATA)) + fputs("\n", f); + + r = sd_bus_creds_get_audit_login_uid(c, &audit_loginuid); + if (r >= 0) + fprintf(f, "%sAuditLoginUID=%s"UID_FMT"%s", prefix, color, audit_loginuid, suffix); + else if (r != -ENODATA) + fprintf(f, "%sAuditLoginUID=%sn/a%s", prefix, color, suffix); + q = sd_bus_creds_get_audit_session_id(c, &audit_sessionid); + if (q >= 0) + fprintf(f, "%sAuditSessionID=%s%"PRIu32"%s", prefix, color, audit_sessionid, suffix); + else if (q != -ENODATA) + fprintf(f, "%sAuditSessionID=%sn/a%s", prefix, color, suffix); + + if (terse && (r != -ENODATA || q != -ENODATA)) + fputs("\n", f); + + if (c->mask & SD_BUS_CREDS_UNIQUE_NAME) + fprintf(f, "%sUniqueName=%s%s%s", prefix, color, c->unique_name, suffix); + + if (sd_bus_creds_get_well_known_names(c, &well_known) >= 0) { + char **i; + + fprintf(f, "%sWellKnownNames=%s", prefix, color); + STRV_FOREACH(i, well_known) { + if (i != well_known) + fputc(' ', f); + + fputs(*i, f); + } + + fprintf(f, "%s", suffix); + } + + if (terse && (c->mask & SD_BUS_CREDS_UNIQUE_NAME || well_known)) + fputc('\n', f); + + dump_capabilities(c, f, "EffectiveCapabilities", terse, sd_bus_creds_has_effective_cap); + dump_capabilities(c, f, "PermittedCapabilities", terse, sd_bus_creds_has_permitted_cap); + dump_capabilities(c, f, "InheritableCapabilities", terse, sd_bus_creds_has_inheritable_cap); + dump_capabilities(c, f, "BoundingCapabilities", terse, sd_bus_creds_has_bounding_cap); + + return 0; +} + +/* + * For details about the file format, see: + * + * http://wiki.wireshark.org/Development/LibpcapFileFormat + */ + +typedef struct _packed_ pcap_hdr_s { + uint32_t magic_number; /* magic number */ + uint16_t version_major; /* major version number */ + uint16_t version_minor; /* minor version number */ + int32_t thiszone; /* GMT to local correction */ + uint32_t sigfigs; /* accuracy of timestamps */ + uint32_t snaplen; /* max length of captured packets, in octets */ + uint32_t network; /* data link type */ +} pcap_hdr_t ; + +typedef struct _packed_ pcaprec_hdr_s { + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ +} pcaprec_hdr_t; + +int bus_pcap_header(size_t snaplen, FILE *f) { + + pcap_hdr_t hdr = { + .magic_number = 0xa1b2c3d4U, + .version_major = 2, + .version_minor = 4, + .thiszone = 0, /* UTC */ + .sigfigs = 0, + .network = 231, /* D-Bus */ + }; + + if (!f) + f = stdout; + + assert(snaplen > 0); + assert((size_t) (uint32_t) snaplen == snaplen); + + hdr.snaplen = (uint32_t) snaplen; + + fwrite(&hdr, 1, sizeof(hdr), f); + + return fflush_and_check(f); +} + +int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) { + struct bus_body_part *part; + pcaprec_hdr_t hdr = {}; + struct timeval tv; + unsigned i; + size_t w; + + if (!f) + f = stdout; + + assert(m); + assert(snaplen > 0); + assert((size_t) (uint32_t) snaplen == snaplen); + + if (m->realtime != 0) + timeval_store(&tv, m->realtime); + else + assert_se(gettimeofday(&tv, NULL) >= 0); + + hdr.ts_sec = tv.tv_sec; + hdr.ts_usec = tv.tv_usec; + hdr.orig_len = BUS_MESSAGE_SIZE(m); + hdr.incl_len = MIN(hdr.orig_len, snaplen); + + /* write the pcap header */ + fwrite(&hdr, 1, sizeof(hdr), f); + + /* write the dbus header */ + w = MIN(BUS_MESSAGE_BODY_BEGIN(m), snaplen); + fwrite(m->header, 1, w, f); + snaplen -= w; + + /* write the dbus body */ + MESSAGE_FOREACH_PART(part, i, m) { + if (snaplen <= 0) + break; + + w = MIN(part->size, snaplen); + fwrite(part->data, 1, w, f); + snaplen -= w; + } + + return fflush_and_check(f); +} diff --git a/src/libsystemd/src/sd-bus/bus-dump.h b/src/libsystemd/src/sd-bus/bus-dump.h new file mode 100644 index 0000000000..68fa043786 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-dump.h @@ -0,0 +1,37 @@ +#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 . +***/ + +#include +#include + +#include + +enum { + BUS_MESSAGE_DUMP_WITH_HEADER = 1, + BUS_MESSAGE_DUMP_SUBTREE_ONLY = 2, +}; + +int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags); + +int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse); + +int bus_pcap_header(size_t snaplen, FILE *f); +int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f); diff --git a/src/libsystemd/src/sd-bus/bus-error.c b/src/libsystemd/src/sd-bus/bus-error.c new file mode 100644 index 0000000000..b6bb0c4a83 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-error.c @@ -0,0 +1,608 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "bus-error.h" +#include "errno-list.h" +#include "string-util.h" +#include "util.h" + +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = { + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoMemory", ENOMEM), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoServer", EHOSTDOWN), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Timeout", ETIMEDOUT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoNetwork", ENONET), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Disconnected", ECONNRESET), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidArgs", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileNotFound", ENOENT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileExists", EEXIST), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownMethod", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownObject", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownInterface", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownProperty", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.PropertyReadOnly", EROFS), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidSignature", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidFileContent", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY), + SD_BUS_ERROR_MAP_END +}; + +/* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section. + * Hide them; for currently unknown reasons they get exported to the shared libries + * even without being listed in the sym file. */ +extern const sd_bus_error_map __start_BUS_ERROR_MAP[] _hidden_; +extern const sd_bus_error_map __stop_BUS_ERROR_MAP[] _hidden_; + +/* Additional maps registered with sd_bus_error_add_map() are in this + * NULL terminated array */ +static const sd_bus_error_map **additional_error_maps = NULL; + +static int bus_error_name_to_errno(const char *name) { + const sd_bus_error_map **map, *m; + const char *p; + int r; + + if (!name) + return EINVAL; + + p = startswith(name, "System.Error."); + if (p) { + r = errno_from_name(p); + if (r < 0) + return EIO; + + return r; + } + + if (additional_error_maps) + for (map = additional_error_maps; *map; map++) + for (m = *map;; m++) { + /* For additional error maps the end marker is actually the end marker */ + if (m->code == BUS_ERROR_MAP_END_MARKER) + break; + + if (streq(m->name, name)) + return m->code; + } + + m = __start_BUS_ERROR_MAP; + while (m < __stop_BUS_ERROR_MAP) { + /* For magic ELF error maps, the end marker might + * appear in the middle of things, since multiple maps + * might appear in the same section. Hence, let's skip + * over it, but realign the pointer to the next 8 byte + * boundary, which is the selected alignment for the + * arrays. */ + if (m->code == BUS_ERROR_MAP_END_MARKER) { + m = ALIGN8_PTR(m+1); + continue; + } + + if (streq(m->name, name)) + return m->code; + + m++; + } + + 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 EOPNOTSUPP: + 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) { + assert_return(!bus_error_is_dirty(e), -EINVAL); + + e->name = strdup(name); + if (!e->name) { + *e = BUS_ERROR_OOM; + return -ENOMEM; + } + + /* If we hit OOM on formatting the pretty message, we ignore + * this, since we at least managed to write the error name */ + if (format) + (void) vasprintf((char**) &e->message, format, ap); + + e->_need_free = 1; + } + + 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 (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; +} + +_public_ int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) { + PROTECT_ERRNO; + 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; + + /* Then, let's try to fill in the supplied message */ + + errno = error; /* Make sure that %m resolves to the specified error */ + 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 = sd_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); +} + +static bool map_ok(const sd_bus_error_map *map) { + for (; map->code != BUS_ERROR_MAP_END_MARKER; map++) + if (!map->name || map->code <=0) + return false; + return true; +} + +_public_ int sd_bus_error_add_map(const sd_bus_error_map *map) { + const sd_bus_error_map **maps = NULL; + unsigned n = 0; + + assert_return(map, -EINVAL); + assert_return(map_ok(map), -EINVAL); + + if (additional_error_maps) + for (; additional_error_maps[n] != NULL; n++) + if (additional_error_maps[n] == map) + return 0; + + maps = realloc_multiply(additional_error_maps, sizeof(struct sd_bus_error_map*), n + 2); + if (!maps) + return -ENOMEM; + + maps[n] = map; + maps[n+1] = NULL; + + additional_error_maps = maps; + return 1; +} diff --git a/src/libsystemd/src/sd-bus/bus-error.h b/src/libsystemd/src/sd-bus/bus-error.h new file mode 100644 index 0000000000..b7aedf406d --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-error.h @@ -0,0 +1,64 @@ +#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 . +***/ + +#include + +#include + +#include "macro.h" + +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); + +#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") + +/* + * There are two ways to register error maps with the error translation + * logic: by using BUS_ERROR_MAP_ELF_REGISTER, which however only + * works when linked into the same ELF module, or via + * sd_bus_error_add_map() which is the official, external API, that + * works from any module. + * + * Note that BUS_ERROR_MAP_ELF_REGISTER has to be used as decorator in + * the bus error table, and BUS_ERROR_MAP_ELF_USE has to be used at + * least once per compilation unit (i.e. per library), to ensure that + * the error map is really added to the final binary. + */ + +#define BUS_ERROR_MAP_ELF_REGISTER \ + __attribute__ ((__section__("BUS_ERROR_MAP"))) \ + __attribute__ ((__used__)) \ + __attribute__ ((aligned(8))) + +#define BUS_ERROR_MAP_ELF_USE(errors) \ + extern const sd_bus_error_map errors[]; \ + __attribute__ ((used)) static const sd_bus_error_map * const CONCATENATE(errors ## _copy_, __COUNTER__) = errors; + +/* We use something exotic as end marker, to ensure people build the + * maps using the macsd-ros. */ +#define BUS_ERROR_MAP_END_MARKER -'x' + +BUS_ERROR_MAP_ELF_USE(bus_standard_errors); diff --git a/src/libsystemd/src/sd-bus/bus-gvariant.c b/src/libsystemd/src/sd-bus/bus-gvariant.c new file mode 100644 index 0000000000..58782767fa --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-gvariant.c @@ -0,0 +1,311 @@ +/*** + 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 . +***/ + +#include "bus-gvariant.h" +#include "bus-signature.h" +#include "bus-type.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: { + if (n == 2) { + /* unary type () has fixed size of 1 */ + r = 1; + } else { + 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; +} + +size_t bus_gvariant_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; +} + +size_t bus_gvariant_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"); +} + +void bus_gvariant_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); +} diff --git a/src/libsystemd/src/sd-bus/bus-gvariant.h b/src/libsystemd/src/sd-bus/bus-gvariant.h new file mode 100644 index 0000000000..6da637fb05 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-gvariant.h @@ -0,0 +1,30 @@ +#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 . +***/ + +#include "macro.h" + +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_; + +size_t bus_gvariant_determine_word_size(size_t sz, size_t extra); +void bus_gvariant_write_word_le(void *p, size_t sz, size_t value); +size_t bus_gvariant_read_word_le(void *p, size_t sz); diff --git a/src/libsystemd/src/sd-bus/bus-internal.c b/src/libsystemd/src/sd-bus/bus-internal.c new file mode 100644 index 0000000000..caca679086 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-internal.c @@ -0,0 +1,374 @@ +/*** + 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 . +***/ + +#include "alloc-util.h" +#include "bus-internal.h" +#include "bus-message.h" +#include "hexdecoct.h" +#include "string-util.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; +} + +char* service_name_startswith(const char *a, const char *b) { + const char *p; + + if (!service_name_is_valid(a) || + !service_name_is_valid(b)) + return NULL; + + p = startswith(a, b); + if (!p) + return NULL; + + if (*p == 0) + return (char*) p; + + if (*p == '.') + return (char*) p + 1; + + return NULL; +} + +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; +} + +/* + * Complex pattern match + * This checks whether @a is a 'complex-prefix' of @b, or @b is a + * 'complex-prefix' of @a, based on strings that consist of labels with @c as + * spearator. This function returns true if: + * - both strings are equal + * - either is a prefix of the other and ends with @c + * The second rule makes sure that either string needs to be fully included in + * the other, and the string which is considered the prefix needs to end with a + * separator. + */ +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)); + + 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); +} + +/* + * Simple pattern match + * This checks whether @a is a 'simple-prefix' of @b, based on strings that + * consist of labels with @c as separator. This function returns true, if: + * - if @a and @b are equal + * - if @a is a prefix of @b, and the first following character in @b (or the + * last character in @a) is @c + * The second rule basically makes sure that if @a is a prefix of @b, then @b + * must follow with a new label separated by @c. It cannot extend the label. + */ +static bool simple_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 *a == 0 && (*b == c || separator); + + if (*a == 0) + return true; + + separator = *a == c; + + 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; +} + +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/src/sd-bus/bus-internal.h b/src/libsystemd/src/sd-bus/bus-internal.h new file mode 100644 index 0000000000..8c4c6fa772 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-internal.h @@ -0,0 +1,399 @@ +#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 . +***/ + +#include +#include + +#include + +#include "bus-error.h" +#include "bus-kernel.h" +#include "bus-match.h" +#include "hashmap.h" +#include "kdbus.h" +#include "list.h" +#include "prioq.h" +#include "refcnt.h" +#include "socket-util.h" +#include "util.h" + +struct reply_callback { + sd_bus_message_handler_t callback; + usec_t timeout; + uint64_t cookie; + unsigned prioq_idx; +}; + +struct filter_callback { + sd_bus_message_handler_t callback; + + unsigned last_iteration; + + LIST_FIELDS(struct filter_callback, callbacks); +}; + +struct match_callback { + sd_bus_message_handler_t callback; + + uint64_t cookie; + unsigned last_iteration; + + char *match_string; + + struct bus_match_node *match_node; +}; + +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); + LIST_HEAD(struct node_object_manager, object_managers); +}; + +struct node_callback { + struct node *node; + + bool is_fallback; + sd_bus_message_handler_t callback; + + unsigned last_iteration; + + LIST_FIELDS(struct node_callback, callbacks); +}; + +struct node_enumerator { + struct node *node; + + sd_bus_node_enumerator_t callback; + + unsigned last_iteration; + + LIST_FIELDS(struct node_enumerator, enumerators); +}; + +struct node_object_manager { + struct node *node; + + LIST_FIELDS(struct node_object_manager, object_managers); +}; + +struct node_vtable { + struct node *node; + + char *interface; + bool is_fallback; + const sd_bus_vtable *vtable; + 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; +}; + +typedef enum BusSlotType { + BUS_REPLY_CALLBACK, + BUS_FILTER_CALLBACK, + BUS_MATCH_CALLBACK, + BUS_NODE_CALLBACK, + BUS_NODE_ENUMERATOR, + BUS_NODE_VTABLE, + BUS_NODE_OBJECT_MANAGER, + _BUS_SLOT_INVALID = -1, +} BusSlotType; + +struct sd_bus_slot { + unsigned n_ref; + sd_bus *bus; + void *userdata; + BusSlotType type:5; + bool floating:1; + bool match_added:1; + char *description; + + LIST_FIELDS(sd_bus_slot, slots); + + union { + struct reply_callback reply_callback; + struct filter_callback filter_callback; + struct match_callback match_callback; + struct node_callback node_callback; + struct node_enumerator node_enumerator; + struct node_object_manager node_object_manager; + struct node_vtable node_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 fake_pids_valid:1; + bool manual_peer_interface:1; + bool is_system:1; + bool is_user:1; + bool allow_interactive_authorization: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; + OrderedHashmap *reply_callbacks; + LIST_HEAD(struct filter_callback, filter_callbacks); + + Hashmap *nodes; + Hashmap *vtable_methods; + Hashmap *vtable_properties; + + union sockaddr_union sockaddr; + socklen_t sockaddr_size; + + char *kernel; + char *machine; + pid_t nspid; + + 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; + + uint64_t creds_mask; + + int *fds; + unsigned n_fds; + + char *exec_path; + char **exec_argv; + + 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_message; + sd_bus_slot *current_slot; + sd_bus_message_handler_t current_handler; + void *current_userdata; + + sd_bus **default_bus_ptr; + pid_t tid; + + struct kdbus_creds fake_creds; + struct kdbus_pids fake_pids; + char *fake_label; + + char *cgroup_root; + + char *description; + + size_t bloom_size; + unsigned bloom_n_hash; + + sd_bus_track *track_queue; + + LIST_HEAD(sd_bus_slot, slots); +}; + +#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_; +char* service_name_startswith(const char *a, const char *b); +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_(sd_bus_unrefp) _unused_ sd_bus *_dont_destroy_##bus = sd_bus_ref(bus) + +int bus_set_address_system(sd_bus *bus); +int bus_set_address_user(sd_bus *bus); +int bus_set_address_system_remote(sd_bus *b, const char *host); +int bus_set_address_system_machine(sd_bus *b, const char *machine); + +int bus_remove_match_by_string(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata); + +int bus_get_root_path(sd_bus *bus); + +int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error); + +#define bus_assert_return(expr, r, error) \ + do { \ + if (!assert_log(expr, #expr)) \ + return sd_bus_error_set_errno(error, r); \ + } while (false) diff --git a/src/libsystemd/src/sd-bus/bus-introspect.c b/src/libsystemd/src/sd-bus/bus-introspect.c new file mode 100644 index 0000000000..8f93edb8da --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-introspect.c @@ -0,0 +1,212 @@ +/*** + 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 . +***/ + +#include "bus-internal.h" +#include "bus-introspect.h" +#include "bus-protocol.h" +#include "bus-signature.h" +#include "fd-util.h" +#include "fileio.h" +#include "string-util.h" +#include "util.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 + "\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, " \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(" \n", i->f); + + if (type == _SD_BUS_VTABLE_METHOD && (flags & SD_BUS_VTABLE_METHOD_NO_REPLY)) + fputs(" \n", i->f); + + if (type == _SD_BUS_VTABLE_PROPERTY || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) { + if (flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT) + fputs(" \n", i->f); + + if (flags & SD_BUS_VTABLE_PROPERTY_CONST) + fputs(" \n", i->f); + else if (flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) + fputs(" \n", i->f); + else if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) + fputs(" \n", i->f); + } + + if (!i->trusted && + (type == _SD_BUS_VTABLE_METHOD || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) && + !(flags & SD_BUS_VTABLE_UNPRIVILEGED)) + fputs(" \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, " 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(" \n", i->f); + break; + + case _SD_BUS_VTABLE_METHOD: + fprintf(i->f, " \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(" \n", i->f); + break; + + case _SD_BUS_VTABLE_PROPERTY: + case _SD_BUS_VTABLE_WRITABLE_PROPERTY: + fprintf(i->f, " \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(" \n", i->f); + break; + + case _SD_BUS_VTABLE_SIGNAL: + fprintf(i->f, " \n", v->x.signal.member); + introspect_write_arguments(i, strempty(v->x.signal.signature), NULL); + introspect_write_flags(i, v->type, v->flags); + fputs(" \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("\n", i->f); + + r = fflush_and_check(i->f); + if (r < 0) + return r; + + 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); + + safe_fclose(i->f); + + free(i->introspection); + zero(*i); +} diff --git a/src/libsystemd/src/sd-bus/bus-introspect.h b/src/libsystemd/src/sd-bus/bus-introspect.h new file mode 100644 index 0000000000..87ac03b26a --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-introspect.h @@ -0,0 +1,40 @@ +#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 . +***/ + +#include + +#include + +#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/src/sd-bus/bus-kernel.c b/src/libsystemd/src/sd-bus/bus-kernel.c new file mode 100644 index 0000000000..59398b841d --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-kernel.c @@ -0,0 +1,1782 @@ +/*** + 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 . +***/ + +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include +#endif + +#include +#include +#include +#include + +/* When we include libgen.h because we need dirname() we immediately + * undefine basename() since libgen.h defines it as a macro to the POSIX + * version which is really broken. We prefer GNU basename(). */ +#include +#undef basename + +#include "alloc-util.h" +#include "bus-bloom.h" +#include "bus-internal.h" +#include "bus-kernel.h" +#include "bus-label.h" +#include "bus-message.h" +#include "bus-util.h" +#include "capability-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "formats-util.h" +#include "memfd-util.h" +#include "parse-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "strv.h" +#include "user-util.h" +#include "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 start, 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.start = start; + (*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 struct kdbus_bloom_filter *append_bloom(struct kdbus_item **d, size_t length) { + struct kdbus_item *i; + + assert(d); + + i = ALIGN8_PTR(*d); + + i->size = offsetof(struct kdbus_item, bloom_filter) + + offsetof(struct kdbus_bloom_filter, data) + + length; + i->type = KDBUS_ITEM_BLOOM_FILTER; + + *d = (struct kdbus_item *) ((uint8_t*) i + i->size); + + return &i->bloom_filter; +} + +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 void add_bloom_arg(void *data, size_t size, unsigned n_hash, unsigned i, const char *t) { + char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")]; + char *e; + + assert(data); + assert(size > 0); + assert(i < 64); + assert(t); + + 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(data, size, n_hash, buf, t); + + strcpy(e, "-dot-prefix"); + bloom_add_prefixes(data, size, n_hash, buf, t, '.'); + strcpy(e, "-slash-prefix"); + bloom_add_prefixes(data, size, n_hash, buf, t, '/'); +} + +static void add_bloom_arg_has(void *data, size_t size, unsigned n_hash, unsigned i, const char *t) { + char buf[sizeof("arg")-1 + 2 + sizeof("-has")]; + char *e; + + assert(data); + assert(size > 0); + assert(i < 64); + assert(t); + + e = stpcpy(buf, "arg"); + if (i < 10) + *(e++) = '0' + (char) i; + else { + *(e++) = '0' + (char) (i / 10); + *(e++) = '0' + (char) (i % 10); + } + + strcpy(e, "-has"); + bloom_add_pair(data, size, n_hash, buf, t); +} + +static int bus_message_setup_bloom(sd_bus_message *m, struct kdbus_bloom_filter *bloom) { + void *data; + unsigned i; + int r; + + assert(m); + assert(bloom); + + data = bloom->data; + memzero(data, m->bus->bloom_size); + bloom->generation = 0; + + bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "message-type", bus_message_type_to_string(m->header->type)); + + if (m->interface) + bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "interface", m->interface); + if (m->member) + bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "member", m->member); + if (m->path) { + bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path", m->path); + bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path-slash-prefix", m->path); + bloom_add_prefixes(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path-slash-prefix", m->path, '/'); + } + + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + for (i = 0; i < 64; i++) { + const char *t, *contents; + char type; + + r = sd_bus_message_peek_type(m, &type, &contents); + if (r < 0) + return r; + + if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) { + + /* The bloom filter includes simple strings of any kind */ + r = sd_bus_message_read_basic(m, type, &t); + if (r < 0) + return r; + + add_bloom_arg(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t); + } + + if (type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g")) { + + /* As well as array of simple strings of any kinds */ + r = sd_bus_message_enter_container(m, type, contents); + if (r < 0) + return r; + + while ((r = sd_bus_message_read_basic(m, contents[0], &t)) > 0) + add_bloom_arg_has(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + } else + /* Stop adding to bloom filter as soon as we + * run into the first argument we cannot add + * to it. */ + break; + } + + return 0; +} + +static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { + struct bus_body_part *part; + struct kdbus_item *d; + const char *destination; + bool well_known = false; + uint64_t dst_id; + size_t sz, dl; + unsigned i; + int r; + + assert(b); + assert(m); + assert(m->sealed); + + /* We put this together only once, if this message is reused + * we reuse the earlier-built version */ + if (m->kdbus) + return 0; + + destination = m->destination ?: m->destination_ptr; + + if (destination) { + r = bus_kernel_parse_unique_name(destination, &dst_id); + if (r < 0) + return r; + if (r == 0) { + well_known = true; + + /* verify_destination_id will usually be 0, which makes the kernel + * driver only look at the provided well-known name. Otherwise, + * the kernel will make sure the provided destination id matches + * the owner of the provided well-known-name, and fail if they + * differ. Currently, this is only needed for bus-proxyd. */ + dst_id = m->verify_destination_id; + } + } else + dst_id = KDBUS_DST_ID_BROADCAST; + + sz = offsetof(struct kdbus_msg, items); + + /* Add in fixed header, fields header and payload */ + sz += (1 + m->n_body_parts) * ALIGN8(offsetof(struct kdbus_item, vec) + + MAX(sizeof(struct kdbus_vec), + sizeof(struct kdbus_memfd))); + + /* Add space for bloom filter */ + sz += ALIGN8(offsetof(struct kdbus_item, bloom_filter) + + offsetof(struct kdbus_bloom_filter, data) + + m->bus->bloom_size); + + /* Add in well-known destination header */ + if (well_known) { + dl = strlen(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; + memzero(m->kdbus, sz); + + m->kdbus->flags = + ((m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_EXPECT_REPLY) | + ((m->header->flags & BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_NO_AUTO_START : 0) | + ((m->header->type == SD_BUS_MESSAGE_SIGNAL) ? KDBUS_MSG_SIGNAL : 0); + + m->kdbus->dst_id = dst_id; + m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS; + m->kdbus->cookie = m->header->dbus2.cookie; + m->kdbus->priority = m->priority; + + if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) + m->kdbus->cookie_reply = m->reply_cookie; + else { + struct timespec now; + + assert_se(clock_gettime(CLOCK_MONOTONIC_COARSE, &now) == 0); + m->kdbus->timeout_ns = now.tv_sec * NSEC_PER_SEC + now.tv_nsec + + m->timeout * NSEC_PER_USEC; + } + + d = m->kdbus->items; + + if (well_known) + append_destination(&d, 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 && 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->memfd_offset, 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->header->type == SD_BUS_MESSAGE_SIGNAL) { + struct kdbus_bloom_filter *bloom; + + bloom = append_bloom(&d, m->bus->bloom_size); + r = bus_message_setup_bloom(m, bloom); + 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 void unset_memfds(struct sd_bus_message *m) { + struct bus_body_part *part; + unsigned i; + + assert(m); + + /* Make sure the memfds are not freed twice */ + MESSAGE_FOREACH_PART(part, i, m) + if (part->memfd >= 0) + part->memfd = -1; +} + +static void message_set_timestamp(sd_bus *bus, sd_bus_message *m, const struct kdbus_timestamp *ts) { + assert(bus); + assert(m); + + if (!ts) + return; + + if (!(bus->attach_flags & KDBUS_ATTACH_TIMESTAMP)) + return; + + m->realtime = ts->realtime_ns / NSEC_PER_USEC; + m->monotonic = ts->monotonic_ns / NSEC_PER_USEC; + m->seqnum = ts->seqnum; +} + +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 *header = NULL; + void *footer = NULL; + size_t header_size = 0, footer_size = 0; + size_t n_bytes = 0, idx = 0; + const char *destination = NULL, *seclabel = NULL; + bool last_was_memfd = false; + 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 (!header) { + header = (struct bus_header*)((uint8_t*) k + d->vec.offset); + header_size = d->vec.size; + } + + footer = (uint8_t*) k + d->vec.offset; + footer_size = d->vec.size; + + n_bytes += d->vec.size; + last_was_memfd = false; + break; + + case KDBUS_ITEM_PAYLOAD_MEMFD: + if (!header) /* memfd cannot be first part */ + return -EBADMSG; + + n_bytes += d->memfd.size; + last_was_memfd = true; + 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 (last_was_memfd) /* memfd cannot be last part */ + return -EBADMSG; + + if (!header) + return -EBADMSG; + + if (header_size < sizeof(struct bus_header)) + return -EBADMSG; + + /* on kdbus we only speak native endian gvariant, never dbus1 + * marshalling or reverse endian */ + if (header->version != 2 || + header->endian != BUS_NATIVE_ENDIAN) + return -EPROTOTYPE; + + r = bus_message_from_header( + bus, + header, header_size, + footer, footer_size, + n_bytes, + fds, n_fds, + 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->memfd_offset = d->memfd.start; + part->size = d->memfd.size; + part->sealed = true; + + idx += d->memfd.size; + break; + } + + case KDBUS_ITEM_PIDS: + + /* The PID/TID might be missing, when the data + * is faked by a bus proxy and it lacks that + * information about the real client (since + * SO_PEERCRED is used for that). Also kernel + * namespacing might make some of this data + * unavailable when untranslatable. */ + + if (d->pids.pid > 0) { + m->creds.pid = (pid_t) d->pids.pid; + m->creds.mask |= SD_BUS_CREDS_PID & bus->creds_mask; + } + + if (d->pids.tid > 0) { + m->creds.tid = (pid_t) d->pids.tid; + m->creds.mask |= SD_BUS_CREDS_TID & bus->creds_mask; + } + + if (d->pids.ppid > 0) { + m->creds.ppid = (pid_t) d->pids.ppid; + m->creds.mask |= SD_BUS_CREDS_PPID & bus->creds_mask; + } else if (d->pids.pid == 1) { + m->creds.ppid = 0; + m->creds.mask |= SD_BUS_CREDS_PPID & bus->creds_mask; + } + + break; + + case KDBUS_ITEM_CREDS: + + /* EUID/SUID/FSUID/EGID/SGID/FSGID might be + * missing too (see above). */ + + if ((uid_t) d->creds.uid != UID_INVALID) { + m->creds.uid = (uid_t) d->creds.uid; + m->creds.mask |= SD_BUS_CREDS_UID & bus->creds_mask; + } + + if ((uid_t) d->creds.euid != UID_INVALID) { + m->creds.euid = (uid_t) d->creds.euid; + m->creds.mask |= SD_BUS_CREDS_EUID & bus->creds_mask; + } + + if ((uid_t) d->creds.suid != UID_INVALID) { + m->creds.suid = (uid_t) d->creds.suid; + m->creds.mask |= SD_BUS_CREDS_SUID & bus->creds_mask; + } + + if ((uid_t) d->creds.fsuid != UID_INVALID) { + m->creds.fsuid = (uid_t) d->creds.fsuid; + m->creds.mask |= SD_BUS_CREDS_FSUID & bus->creds_mask; + } + + if ((gid_t) d->creds.gid != GID_INVALID) { + m->creds.gid = (gid_t) d->creds.gid; + m->creds.mask |= SD_BUS_CREDS_GID & bus->creds_mask; + } + + if ((gid_t) d->creds.egid != GID_INVALID) { + m->creds.egid = (gid_t) d->creds.egid; + m->creds.mask |= SD_BUS_CREDS_EGID & bus->creds_mask; + } + + if ((gid_t) d->creds.sgid != GID_INVALID) { + m->creds.sgid = (gid_t) d->creds.sgid; + m->creds.mask |= SD_BUS_CREDS_SGID & bus->creds_mask; + } + + if ((gid_t) d->creds.fsgid != GID_INVALID) { + m->creds.fsgid = (gid_t) d->creds.fsgid; + m->creds.mask |= SD_BUS_CREDS_FSGID & bus->creds_mask; + } + + break; + + case KDBUS_ITEM_TIMESTAMP: + message_set_timestamp(bus, m, &d->timestamp); + 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; + + r = bus_get_root_path(bus); + 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.mask |= SD_BUS_CREDS_AUDIT_SESSION_ID & bus->creds_mask; + + m->creds.audit_login_uid = (uid_t) d->audit.loginuid; + m->creds.mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID & bus->creds_mask; + break; + + case KDBUS_ITEM_CAPS: + if (d->caps.last_cap != cap_last_cap() || + d->size - offsetof(struct kdbus_item, caps.caps) < DIV_ROUND_UP(d->caps.last_cap, 32U) * 4 * 4) { + r = -EBADMSG; + goto fail; + } + + m->creds.capability = d->caps.caps; + 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)) { + r = -EBADMSG; + goto fail; + } + + destination = d->str; + break; + + case KDBUS_ITEM_OWNED_NAME: + if (!service_name_is_valid(d->name.name)) { + r = -EBADMSG; + goto fail; + } + + if (bus->creds_mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { + char **wkn; + size_t n; + + /* We just extend the array here, but + * do not allocate the strings inside + * of it, instead we just point to our + * buffer directly. */ + n = strv_length(m->creds.well_known_names); + wkn = realloc(m->creds.well_known_names, (n + 2) * sizeof(char*)); + if (!wkn) { + r = -ENOMEM; + goto fail; + } + + wkn[n] = d->name.name; + wkn[n+1] = NULL; + m->creds.well_known_names = wkn; + + m->creds.mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; + } + break; + + case KDBUS_ITEM_CONN_DESCRIPTION: + m->creds.description = d->str; + m->creds.mask |= SD_BUS_CREDS_DESCRIPTION & bus->creds_mask; + break; + + case KDBUS_ITEM_AUXGROUPS: + + if (bus->creds_mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + size_t i, n; + gid_t *g; + + n = (d->size - offsetof(struct kdbus_item, data64)) / sizeof(uint64_t); + g = new(gid_t, n); + if (!g) { + r = -ENOMEM; + goto fail; + } + + for (i = 0; i < n; i++) + g[i] = d->data64[i]; + + m->creds.supplementary_gids = g; + m->creds.n_supplementary_gids = n; + m->creds.mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + } + + break; + + case KDBUS_ITEM_FDS: + case KDBUS_ITEM_SECLABEL: + case KDBUS_ITEM_BLOOM_FILTER: + break; + + default: + log_debug("Got unknown field from kernel %llu", d->type); + } + } + + /* If we requested the list of well-known names to be appended + * and the sender had none no item for it will be + * attached. However, this does *not* mean that the kernel + * didn't want to provide this information to us. Hence, let's + * explicitly mark this information as available if it was + * requested. */ + m->creds.mask |= bus->creds_mask & SD_BUS_CREDS_WELL_KNOWN_NAMES; + + r = bus_message_parse_fields(m); + if (r < 0) + goto fail; + + /* Refuse messages if kdbus and dbus1 cookie doesn't match up */ + if ((uint64_t) m->header->dbus2.cookie != k->cookie) { + r = -EBADMSG; + goto fail; + } + + /* Refuse messages where the reply flag doesn't match up */ + if (!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) != !!(k->flags & KDBUS_MSG_EXPECT_REPLY)) { + r = -EBADMSG; + goto fail; + } + + /* Refuse reply messages where the reply cookie doesn't match up */ + if ((m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) && m->reply_cookie != k->cookie_reply) { + r = -EBADMSG; + goto fail; + } + + /* Refuse messages where the autostart flag doesn't match up */ + if (!(m->header->flags & BUS_MESSAGE_NO_AUTO_START) != !(k->flags & KDBUS_MSG_NO_AUTO_START)) { + r = -EBADMSG; + goto fail; + } + + /* Override information from the user header with data from the kernel */ + if (k->src_id == KDBUS_SRC_ID_KERNEL) + bus_message_set_sender_driver(bus, m); + else { + xsprintf(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 { + xsprintf(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: + unset_memfds(m); + sd_bus_message_unref(m); + + return r; +} + +int bus_kernel_take_fd(sd_bus *b) { + struct kdbus_bloom_parameter *bloom = NULL; + struct kdbus_item *items, *item; + struct kdbus_cmd_hello *hello; + _cleanup_free_ char *g = NULL; + const char *name; + size_t l = 0, m = 0, sz; + int r; + + assert(b); + + if (b->is_server) + return -EINVAL; + + b->use_memfd = 1; + + if (b->description) { + g = bus_label_escape(b->description); + if (!g) + return -ENOMEM; + + name = g; + } else { + char pr[17] = {}; + + /* If no name is explicitly set, we'll include a hint + * indicating the library implementation, a hint which + * kind of bus this is and the thread name */ + + assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0); + + if (isempty(pr)) { + name = b->is_system ? "sd-system" : + b->is_user ? "sd-user" : "sd"; + } else { + _cleanup_free_ char *e = NULL; + + e = bus_label_escape(pr); + if (!e) + return -ENOMEM; + + g = strappend(b->is_system ? "sd-system-" : + b->is_user ? "sd-user-" : "sd-", + e); + if (!g) + return -ENOMEM; + + name = g; + } + + b->description = bus_label_unescape(name); + if (!b->description) + return -ENOMEM; + } + + m = strlen(name); + + sz = ALIGN8(offsetof(struct kdbus_cmd_hello, items)) + + ALIGN8(offsetof(struct kdbus_item, str) + m + 1); + + if (b->fake_creds_valid) + sz += ALIGN8(offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds)); + + if (b->fake_pids_valid) + sz += ALIGN8(offsetof(struct kdbus_item, pids) + sizeof(struct kdbus_pids)); + + if (b->fake_label) { + l = strlen(b->fake_label); + sz += ALIGN8(offsetof(struct kdbus_item, str) + l + 1); + } + + hello = alloca0_align(sz, 8); + hello->size = sz; + hello->flags = b->hello_flags; + hello->attach_flags_send = _KDBUS_ATTACH_ANY; + hello->attach_flags_recv = b->attach_flags; + hello->pool_size = KDBUS_POOL_SIZE; + + item = hello->items; + + item->size = offsetof(struct kdbus_item, str) + m + 1; + item->type = KDBUS_ITEM_CONN_DESCRIPTION; + memcpy(item->str, name, m + 1); + item = KDBUS_ITEM_NEXT(item); + + 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_pids_valid) { + item->size = offsetof(struct kdbus_item, pids) + sizeof(struct kdbus_pids); + item->type = KDBUS_ITEM_PIDS; + item->pids = b->fake_pids; + + item = KDBUS_ITEM_NEXT(item); + } + + if (b->fake_label) { + item->size = offsetof(struct kdbus_item, str) + l + 1; + item->type = KDBUS_ITEM_SECLABEL; + memcpy(item->str, b->fake_label, l+1); + } + + r = ioctl(b->input_fd, KDBUS_CMD_HELLO, hello); + if (r < 0) { + if (errno == ENOTTY) + /* If the ioctl is not supported we assume that the + * API version changed in a major incompatible way, + * let's indicate an API incompatibility in this + * case. */ + return -ESOCKTNOSUPPORT; + + 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; + r = -errno; + goto fail; + } + } + + /* The higher 32bit of the bus_flags fields are considered + * 'incompatible flags'. Refuse them all for now. */ + if (hello->bus_flags > 0xFFFFFFFFULL) { + r = -ESOCKTNOSUPPORT; + goto fail; + } + + /* extract bloom parameters from items */ + items = (void*)((uint8_t*)b->kdbus_buffer + hello->offset); + KDBUS_FOREACH(item, items, hello->items_size) { + switch (item->type) { + case KDBUS_ITEM_BLOOM_PARAMETER: + bloom = &item->bloom_parameter; + break; + } + } + + if (!bloom || !bloom_validate_parameters((size_t) bloom->size, (unsigned) bloom->n_hash)) { + r = -EOPNOTSUPP; + goto fail; + } + + b->bloom_size = (size_t) bloom->size; + b->bloom_n_hash = (unsigned) bloom->n_hash; + + if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello->id) < 0) { + r = -ENOMEM; + goto fail; + } + + b->unique_id = hello->id; + + b->is_kernel = true; + b->bus_client = true; + b->can_fds = !!(hello->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)); + + /* free returned items */ + (void) bus_kernel_cmd_free(b, hello->offset); + return bus_start_running(b); + +fail: + (void) bus_kernel_cmd_free(b, hello->offset); + return r; +} + +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_cmd_free(sd_bus *bus, uint64_t offset) { + struct kdbus_cmd_free cmd = { + .size = sizeof(cmd), + .offset = offset, + }; + int r; + + assert(bus); + assert(bus->is_kernel); + + r = ioctl(bus->input_fd, KDBUS_CMD_FREE, &cmd); + if (r < 0) + return -errno; + + return 0; +} + +static void close_kdbus_msg(sd_bus *bus, struct kdbus_msg *k) { + struct kdbus_item *d; + + assert(bus); + assert(k); + + 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) + safe_close(d->memfd.fd); + } + + bus_kernel_cmd_free(bus, (uint8_t*) k - (uint8_t*) bus->kdbus_buffer); +} + +int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call) { + struct kdbus_cmd_send cmd = { }; + 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; + + cmd.size = sizeof(cmd); + cmd.msg_address = (uintptr_t)m->kdbus; + + /* If this is a synchronous method call, then let's tell the + * kernel, so that it can pass CPU time/scheduling to the + * destination for the time, if it wants to. If we + * synchronously wait for the result anyway, we won't need CPU + * anyway. */ + if (hint_sync_call) { + m->kdbus->flags |= KDBUS_MSG_EXPECT_REPLY; + cmd.flags |= KDBUS_SEND_SYNC_REPLY; + } + + r = ioctl(bus->output_fd, KDBUS_CMD_SEND, &cmd); + if (r < 0) { + _cleanup_(sd_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; + + } else if (hint_sync_call) { + struct kdbus_msg *k; + + k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + cmd.reply.offset); + assert(k); + + if (k->payload_type == KDBUS_PAYLOAD_DBUS) { + + r = bus_kernel_make_message(bus, k); + if (r < 0) { + close_kdbus_msg(bus, k); + + /* Anybody can send us invalid messages, let's just drop them. */ + if (r == -EBADMSG || r == -EPROTOTYPE) + log_debug_errno(r, "Ignoring invalid synchronous reply: %m"); + else + return r; + } + } else { + log_debug("Ignoring message with unknown payload type %llu.", (unsigned long long) k->payload_type); + close_kdbus_msg(bus, k); + } + } + + return 1; +} + +static int push_name_owner_changed( + sd_bus *bus, + const char *name, + const char *old_owner, + const char *new_owner, + const struct kdbus_timestamp *ts) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert(bus); + + r = sd_bus_message_new_signal( + bus, + &m, + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "NameOwnerChanged"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "sss", name, old_owner, new_owner); + if (r < 0) + return r; + + bus_message_set_sender_driver(bus, m); + message_set_timestamp(bus, m, ts); + + 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, + const struct kdbus_msg *k, + const struct kdbus_item *d, + const struct kdbus_timestamp *ts) { + + 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_id.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.id); + + if (d->type == KDBUS_ITEM_NAME_REMOVE || (d->name_change.new_id.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.id); + + return push_name_owner_changed(bus, d->name_change.name, old_owner, new_owner, ts); +} + +static int translate_id_change( + sd_bus *bus, + const struct kdbus_msg *k, + const struct kdbus_item *d, + const struct kdbus_timestamp *ts) { + + 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, + ts); +} + +static int translate_reply( + sd_bus *bus, + const struct kdbus_msg *k, + const struct kdbus_item *d, + const struct kdbus_timestamp *ts) { + + _cleanup_(sd_bus_message_unrefp) 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; + + message_set_timestamp(bus, m, ts); + + 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) { + static int (* const translate[])(sd_bus *bus, const struct kdbus_msg *k, const struct kdbus_item *d, const struct kdbus_timestamp *ts) = { + [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, + }; + + struct kdbus_item *d, *found = NULL; + struct kdbus_timestamp *ts = NULL; + + assert(bus); + assert(k); + assert(k->payload_type == KDBUS_PAYLOAD_KERNEL); + + KDBUS_ITEM_FOREACH(d, k, items) { + if (d->type == KDBUS_ITEM_TIMESTAMP) + ts = &d->timestamp; + else 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, ts); +} + +int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority) { + struct kdbus_cmd_recv recv = { .size = sizeof(recv) }; + struct kdbus_msg *k; + int r; + + assert(bus); + + r = bus_rqueue_make_room(bus); + if (r < 0) + return r; + + if (hint_priority) { + recv.flags |= KDBUS_RECV_USE_PRIORITY; + recv.priority = priority; + } + + r = ioctl(bus->input_fd, KDBUS_CMD_RECV, &recv); + if (recv.return_flags & KDBUS_RECV_RETURN_DROPPED_MSGS) + log_debug("%s: kdbus reports %" PRIu64 " dropped broadcast messages, ignoring.", strna(bus->description), (uint64_t) recv.dropped_msgs); + if (r < 0) { + if (errno == EAGAIN) + return 0; + + return -errno; + } + + k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + recv.msg.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_errno(r, "Ignoring invalid message: %m"); + r = 0; + } + + if (r <= 0) + close_kdbus_msg(bus, k); + } else if (k->payload_type == KDBUS_PAYLOAD_KERNEL) { + r = bus_kernel_translate_message(bus, k); + close_kdbus_msg(bus, k); + } else { + log_debug("Ignoring message with unknown payload type %llu.", (unsigned long long) k->payload_type); + 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 -EOPNOTSUPP; + + assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) == 0); + + if (bus->n_memfd_cache <= 0) { + int r; + + assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) == 0); + + r = memfd_new(bus->description); + if (r < 0) + return r; + + *address = NULL; + *mapped = 0; + *allocated = 0; + return r; + } + + 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); + + safe_close(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(memfd_set_size(fd, 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); +} + +uint64_t request_name_flags_to_kdbus(uint64_t flags) { + uint64_t f = 0; + + 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; + + return f; +} + +uint64_t attach_flags_to_kdbus(uint64_t mask) { + uint64_t m = 0; + + if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) + m |= KDBUS_ATTACH_CREDS; + + if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID)) + m |= KDBUS_ATTACH_PIDS; + + if (mask & SD_BUS_CREDS_COMM) + m |= KDBUS_ATTACH_PID_COMM; + + if (mask & SD_BUS_CREDS_TID_COMM) + m |= KDBUS_ATTACH_TID_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; + + if (mask & SD_BUS_CREDS_DESCRIPTION) + m |= KDBUS_ATTACH_CONN_DESCRIPTION; + + if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) + m |= KDBUS_ATTACH_AUXGROUPS; + + return m; +} + +int bus_kernel_create_bus(const char *name, bool world, char **s) { + struct kdbus_cmd *make; + struct kdbus_item *n; + size_t l; + int fd; + + assert(name); + assert(s); + + fd = open("/sys/fs/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + l = strlen(name); + make = alloca0_align(offsetof(struct kdbus_cmd, items) + + ALIGN8(offsetof(struct kdbus_item, bloom_parameter) + sizeof(struct kdbus_bloom_parameter)) + + ALIGN8(offsetof(struct kdbus_item, data64) + sizeof(uint64_t)) + + ALIGN8(offsetof(struct kdbus_item, str) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1), + 8); + + make->size = offsetof(struct kdbus_cmd, items); + + /* Set the bloom parameters */ + n = make->items; + n->size = offsetof(struct kdbus_item, bloom_parameter) + + sizeof(struct kdbus_bloom_parameter); + n->type = KDBUS_ITEM_BLOOM_PARAMETER; + n->bloom_parameter.size = DEFAULT_BLOOM_SIZE; + n->bloom_parameter.n_hash = DEFAULT_BLOOM_N_HASH; + + assert_cc(DEFAULT_BLOOM_SIZE > 0); + assert_cc(DEFAULT_BLOOM_N_HASH > 0); + + make->size += ALIGN8(n->size); + + /* Provide all metadata via bus-owner queries */ + n = KDBUS_ITEM_NEXT(n); + n->type = KDBUS_ITEM_ATTACH_FLAGS_SEND; + n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t); + n->data64[0] = _KDBUS_ATTACH_ANY; + make->size += ALIGN8(n->size); + + /* Set the a good name */ + n = KDBUS_ITEM_NEXT(n); + sprintf(n->str, UID_FMT "-%s", 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 = world ? KDBUS_MAKE_ACCESS_WORLD : 0; + + if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) { + safe_close(fd); + + /* Major API change? then the ioctls got shuffled around. */ + if (errno == ENOTTY) + return -ESOCKTNOSUPPORT; + + return -errno; + } + + if (s) { + char *p; + + p = strjoin("/sys/fs/kdbus/", n->str, "/bus", NULL); + if (!p) { + safe_close(fd); + return -ENOMEM; + } + + *s = p; + } + + return fd; +} + +int bus_kernel_open_bus_fd(const char *bus, char **path) { + char *p; + int fd; + size_t len; + + assert(bus); + + len = strlen("/sys/fs/kdbus/") + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + strlen("/bus") + 1; + + if (path) { + p = new(char, len); + if (!p) + return -ENOMEM; + } else + p = newa(char, len); + + sprintf(p, "/sys/fs/kdbus/" UID_FMT "-%s/bus", getuid(), bus); + + fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) { + if (path) + free(p); + + return -errno; + } + + if (path) + *path = p; + + return fd; +} + +int bus_kernel_try_close(sd_bus *bus) { + struct kdbus_cmd byebye = { .size = sizeof(byebye) }; + + assert(bus); + assert(bus->is_kernel); + + if (ioctl(bus->input_fd, KDBUS_CMD_BYEBYE, &byebye) < 0) + return -errno; + + return 0; +} + +int bus_kernel_drop_one(int fd) { + struct kdbus_cmd_recv recv = { + .size = sizeof(recv), + .flags = KDBUS_RECV_DROP, + }; + + assert(fd >= 0); + + if (ioctl(fd, KDBUS_CMD_RECV, &recv) < 0) + return -errno; + + return 0; +} + +int bus_kernel_realize_attach_flags(sd_bus *bus) { + struct kdbus_cmd *update; + struct kdbus_item *n; + + assert(bus); + assert(bus->is_kernel); + + update = alloca0_align(offsetof(struct kdbus_cmd, items) + + ALIGN8(offsetof(struct kdbus_item, data64) + sizeof(uint64_t)), + 8); + + n = update->items; + n->type = KDBUS_ITEM_ATTACH_FLAGS_RECV; + n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t); + n->data64[0] = bus->attach_flags; + + update->size = + offsetof(struct kdbus_cmd, items) + + ALIGN8(n->size); + + if (ioctl(bus->input_fd, KDBUS_CMD_UPDATE, update) < 0) + return -errno; + + return 0; +} + +int bus_kernel_get_bus_name(sd_bus *bus, char **name) { + struct kdbus_cmd_info cmd = { + .size = sizeof(struct kdbus_cmd_info), + }; + struct kdbus_info *info; + struct kdbus_item *item; + char *n = NULL; + int r; + + assert(bus); + assert(name); + assert(bus->is_kernel); + + r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); + if (r < 0) + return -errno; + + info = (struct kdbus_info*) ((uint8_t*) bus->kdbus_buffer + cmd.offset); + + KDBUS_ITEM_FOREACH(item, info, items) + if (item->type == KDBUS_ITEM_MAKE_NAME) { + r = free_and_strdup(&n, item->str); + break; + } + + bus_kernel_cmd_free(bus, cmd.offset); + + if (r < 0) + return r; + if (!n) + return -EIO; + + *name = n; + return 0; +} diff --git a/src/libsystemd/src/sd-bus/bus-kernel.h b/src/libsystemd/src/sd-bus/bus-kernel.h new file mode 100644 index 0000000000..2927ba26a5 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-kernel.h @@ -0,0 +1,93 @@ +#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 . +***/ + +#include + +#include + +#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) && \ + ((uint8_t *) part >= (uint8_t *) head); \ + part = KDBUS_ITEM_NEXT(part)) +#define KDBUS_FOREACH(iter, first, _size) \ + for (iter = (first); \ + ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ + ((uint8_t *)(iter) >= (uint8_t *)(first)); \ + iter = (void*)(((uint8_t *)iter) + ALIGN8((iter)->size))) + +#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 (512*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, bool hint_sync_call); +int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority); + +int bus_kernel_open_bus_fd(const char *bus, char **path); + +int bus_kernel_create_bus(const char *name, bool world, char **s); +int bus_kernel_create_endpoint(const char *bus_name, const char *ep_name, char **path); + +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); + +uint64_t request_name_flags_to_kdbus(uint64_t sd_bus_flags); +uint64_t attach_flags_to_kdbus(uint64_t sd_bus_flags); + +int bus_kernel_try_close(sd_bus *bus); + +int bus_kernel_drop_one(int fd); + +int bus_kernel_realize_attach_flags(sd_bus *bus); + +int bus_kernel_get_bus_name(sd_bus *bus, char **name); + +int bus_kernel_cmd_free(sd_bus *bus, uint64_t offset); diff --git a/src/libsystemd/src/sd-bus/bus-match.c b/src/libsystemd/src/sd-bus/bus-match.c new file mode 100644 index 0000000000..397baf6f33 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-match.c @@ -0,0 +1,1218 @@ +/*** + 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 . +***/ + +#include "alloc-util.h" +#include "bus-internal.h" +#include "bus-match.h" +#include "bus-message.h" +#include "bus-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "hexdecoct.h" +#include "string-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_HAS_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) || + (t >= BUS_MATCH_ARG_HAS && t <= BUS_MATCH_ARG_HAS_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->type == BUS_MATCH_ROOT) + return false; + + 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, + char **value_strv, + 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: + + if (value_str) + return streq_ptr(node->value.str, value_str); + + return false; + + case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: { + char **i; + + STRV_FOREACH(i, value_strv) + if (streq_ptr(node->value.str, *i)) + return true; + + return false; + } + + case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: + if (value_str) + return namespace_simple_pattern(node->value.str, value_str); + + return false; + + case BUS_MATCH_PATH_NAMESPACE: + return path_simple_pattern(node->value.str, value_str); + + case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: + if (value_str) + return path_complex_pattern(node->value.str, value_str); + + return false; + + 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_HAS ... BUS_MATCH_ARG_HAS_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) { + + _cleanup_strv_free_ char **test_strv = NULL; + 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.callback->last_iteration == bus->iteration_counter) + return 0; + + node->leaf.callback->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->callback) { + _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; + sd_bus_slot *slot; + + slot = container_of(node->leaf.callback, sd_bus_slot, match_callback); + if (bus) { + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = node->leaf.callback->callback; + bus->current_userdata = slot->userdata; + } + r = node->leaf.callback->callback(m, slot->userdata, &error_buffer); + if (bus) { + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + } + + r = bus_maybe_reply_error(m, r, &error_buffer); + if (r != 0) + return r; + + if (bus && bus->match_callbacks_modified) + return 0; + } + + 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: + (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str); + break; + + case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: + (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str); + break; + + case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: + (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str); + break; + + case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: + (void) bus_message_get_arg_strv(m, node->type - BUS_MATCH_ARG_HAS, &test_strv); + 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 (test_strv) { + char **i; + + STRV_FOREACH(i, test_strv) { + found = hashmap_get(node->compare.children, *i); + if (found) { + r = bus_match_run(bus, found, m); + if (r != 0) + return r; + } + } + + found = NULL; + } 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, test_strv, 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(NULL); + if (!c->compare.children) { + r = -ENOMEM; + goto fail; + } + } else if (BUS_MATCH_CAN_HASH(t)) { + c->compare.children = hashmap_new(&string_hash_ops); + 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; n && !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, + struct match_callback *callback) { + + struct bus_match_node *n; + + assert(where); + assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE); + assert(callback); + + 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; + callback->match_node = n; + + where->child = 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) { + sd_bus_slot *s; + + s = container_of(c->leaf.callback, sd_bus_slot, match_callback); + + if (c->type == BUS_MATCH_LEAF && + c->leaf.callback->callback == callback && + s->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; + } + + if (n == 7 && startswith(k, "arg") && startswith(k + 4, "has")) { + int j; + + j = undecchar(k[3]); + if (j < 0) + return -EINVAL; + + return BUS_MATCH_ARG_HAS + j; + } + + if (n == 8 && startswith(k, "arg") && startswith(k + 5, "has")) { + 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_HAS + a * 10 + b; + if (t > BUS_MATCH_ARG_HAS_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; + + /* Avahi's match rules appear to include whitespace, skip over it */ + p += strspn(p, " "); + + 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; + + value = mfree(value); + } 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[quoted] == 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_fclose_ FILE *f = NULL; + char *buffer = NULL; + size_t size = 0; + unsigned i; + int r; + + 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); + } + + r = fflush_and_check(f); + if (r < 0) + return NULL; + + return buffer; +} + +int bus_match_add( + struct bus_match_node *root, + struct bus_match_component *components, + unsigned n_components, + struct match_callback *callback) { + + unsigned i; + struct bus_match_node *n; + int r; + + assert(root); + assert(callback); + + 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; + } + + return bus_match_add_leaf(n, callback); +} + +int bus_match_remove( + struct bus_match_node *root, + struct match_callback *callback) { + + struct bus_match_node *node, *pp; + + assert(root); + assert(callback); + + node = callback->match_node; + if (!node) + return 0; + + assert(node->type == BUS_MATCH_LEAF); + + callback->match_node = NULL; + + /* Free the leaf */ + pp = node->parent; + bus_match_node_free(node); + + /* Prune the tree above */ + while (pp) { + node = pp; + pp = node->parent; + + if (!bus_match_node_maybe_free(node)) + break; + } + + return 1; +} + +int bus_match_find( + struct bus_match_node *root, + struct bus_match_component *components, + unsigned n_components, + sd_bus_message_handler_t callback, + void *userdata, + struct match_callback **ret) { + + struct bus_match_node *n, **gc; + unsigned i; + int r; + + assert(root); + assert(ret); + + 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; + + *ret = n->leaf.callback; + return 1; +} + +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; + + case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: + snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS); + 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->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->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); +} + +enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) { + bool found_driver = false; + unsigned i; + + if (n_components <= 0) + return BUS_MATCH_GENERIC; + + assert(components); + + /* Checks whether the specified match can only match the + * pseudo-service for local messages, which we detect by + * sender, interface or path. If a match is not restricted to + * local messages, then we check if it only matches on the + * driver. */ + + for (i = 0; i < n_components; i++) { + const struct bus_match_component *c = components + i; + + if (c->type == BUS_MATCH_SENDER) { + if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local")) + return BUS_MATCH_LOCAL; + + if (streq_ptr(c->value_str, "org.freedesktop.DBus")) + found_driver = true; + } + + if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local")) + return BUS_MATCH_LOCAL; + + if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local")) + return BUS_MATCH_LOCAL; + } + + return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC; + +} diff --git a/src/libsystemd/src/sd-bus/bus-match.h b/src/libsystemd/src/sd-bus/bus-match.h new file mode 100644 index 0000000000..3f71720185 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-match.h @@ -0,0 +1,100 @@ +#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 . +***/ + +#include + +#include "hashmap.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_ARG_HAS, + BUS_MATCH_ARG_HAS_LAST = BUS_MATCH_ARG_HAS + 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 { + struct match_callback *callback; + } 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; +}; + +enum bus_match_scope { + BUS_MATCH_GENERIC, + BUS_MATCH_LOCAL, + BUS_MATCH_DRIVER, +}; + +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, struct match_callback *callback); +int bus_match_remove(struct bus_match_node *root, struct match_callback *callback); + +int bus_match_find(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, sd_bus_message_handler_t callback, void *userdata, struct match_callback **ret); + +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); + +enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components); diff --git a/src/libsystemd/src/sd-bus/bus-message.c b/src/libsystemd/src/sd-bus/bus-message.c new file mode 100644 index 0000000000..a9359c1528 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-message.c @@ -0,0 +1,5939 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "bus-gvariant.h" +#include "bus-internal.h" +#include "bus-message.h" +#include "bus-signature.h" +#include "bus-type.h" +#include "bus-util.h" +#include "fd-util.h" +#include "io-util.h" +#include "memfd-util.h" +#include "string-util.h" +#include "strv.h" +#include "time-util.h" +#include "utf8.h" +#include "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) { + assert(part->memfd_offset == 0); + assert(part->data == part->mmap_begin); + bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped, part->allocated); + } else { + if (part->mapped > 0) + assert_se(munmap(part->mmap_begin, part->mapped) == 0); + + safe_close(part->memfd); + } + + } else if (part->munmap_this) + munmap(part->mmap_begin, 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); + } + + m->containers = mfree(m->containers); + + 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->release_kdbus) + bus_kernel_cmd_free(m->bus, (uint8_t *) m->kdbus - (uint8_t *) m->bus->kdbus_buffer); + + if (m->free_kdbus) + free(m->kdbus); + + 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); + + m->destination_ptr = mfree(m->destination_ptr); + 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->fields_size; + start = ALIGN_TO(old_size, align); + new_size = start + sz; + + if (new_size < start || + new_size > (size_t) ((uint32_t) -1)) + goto poison; + + if (old_size == new_size) + return (uint8_t*) m->header + old_size; + + 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) + memzero((uint8_t*) np + old_size, start - old_size); + + op = m->header; + m->header = np; + m->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, + uint64_t h, + char type, + const char *s, + const char **ret) { + + size_t l; + uint8_t *p; + + assert(m); + + /* dbus1 only allows 8bit header field ids */ + if (h > 0xFF) + return -EINVAL; + + /* 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 64bit, ((string + NUL) + NUL + signature string 's') */ + p = message_extend_fields(m, 8, 8 + l + 1 + 1 + 1, true); + if (!p) + return -ENOMEM; + + *((uint64_t*) p) = h; + 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] = (uint8_t) 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, + uint64_t h, + const char *s, + const char **ret) { + + size_t l; + uint8_t *p; + + assert(m); + + /* dbus1 only allows 8bit header field ids */ + if (h > 0xFF) + return -EINVAL; + + /* dbus1 doesn't allow signatures over 8bit, 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] = (uint8_t) 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, uint64_t h, uint32_t x) { + uint8_t *p; + + assert(m); + + /* dbus1 only allows 8bit header field ids */ + if (h > 0xFF) + return -EINVAL; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + /* (field id 64bit + ((value + NUL + signature string 'u') */ + + p = message_extend_fields(m, 8, 8 + 4 + 1 + 1, true); + if (!p) + return -ENOMEM; + + *((uint64_t*) p) = h; + *((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] = (uint8_t) h; + p[1] = 1; + p[2] = 'u'; + p[3] = 0; + + ((uint32_t*) p)[1] = x; + } + + return 0; +} + +static int message_append_field_uint64(sd_bus_message *m, uint64_t h, uint64_t x) { + uint8_t *p; + + assert(m); + + /* dbus1 only allows 8bit header field ids */ + if (h > 0xFF) + return -EINVAL; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + /* (field id 64bit + ((value + NUL + signature string 't') */ + + p = message_extend_fields(m, 8, 8 + 8 + 1 + 1, true); + if (!p) + return -ENOMEM; + + *((uint64_t*) p) = h; + *((uint64_t*) (p + 8)) = x; + p[16] = 0; + p[17] = 't'; + } else { + /* (field id byte + (signature length + signature 't' + NUL) + 4 byte padding + value) */ + p = message_extend_fields(m, 8, 4 + 4 + 8, false); + if (!p) + return -ENOMEM; + + p[0] = (uint8_t) h; + p[1] = 1; + p[2] = 't'; + p[3] = 0; + p[4] = 0; + p[5] = 0; + p[6] = 0; + p[7] = 0; + + ((uint64_t*) p)[1] = x; + } + + return 0; +} + +static int message_append_reply_cookie(sd_bus_message *m, uint64_t cookie) { + assert(m); + + if (BUS_MESSAGE_IS_GVARIANT(m)) + return message_append_field_uint64(m, BUS_MESSAGE_HEADER_REPLY_SERIAL, cookie); + else { + /* 64bit cookies are not supported on dbus1 */ + if (cookie > 0xffffffffUL) + return -EOPNOTSUPP; + + return message_append_field_uint32(m, BUS_MESSAGE_HEADER_REPLY_SERIAL, (uint32_t) cookie); + } +} + +int bus_message_from_header( + sd_bus *bus, + void *header, + size_t header_accessible, + void *footer, + size_t footer_accessible, + size_t message_size, + int *fds, + unsigned n_fds, + const char *label, + size_t extra, + sd_bus_message **ret) { + + _cleanup_free_ sd_bus_message *m = NULL; + struct bus_header *h; + size_t a, label_sz; + + assert(bus); + assert(header || header_accessible <= 0); + assert(footer || footer_accessible <= 0); + assert(fds || n_fds <= 0); + assert(ret); + + if (header_accessible < sizeof(struct bus_header)) + return -EBADMSG; + + if (header_accessible > message_size) + return -EBADMSG; + if (footer_accessible > message_size) + return -EBADMSG; + + h = header; + if (!IN_SET(h->version, 1, 2)) + return -EBADMSG; + + if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID) + return -EBADMSG; + + if (!IN_SET(h->endian, BUS_LITTLE_ENDIAN, BUS_BIG_ENDIAN)) + return -EBADMSG; + + /* Note that we are happy with unknown flags in the flags header! */ + + 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 = header; + m->header_accessible = header_accessible; + m->footer = footer; + m->footer_accessible = footer_accessible; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + size_t ws; + + if (h->dbus2.cookie == 0) + return -EBADMSG; + + /* dbus2 derives the sizes from the message size and + the offset table at the end, since it is formatted as + gvariant "yyyyuta{tv}v". Since the message itself is a + structure with precisely to variable sized entries, + there's only one offset in the table, which marks the + end of the fields array. */ + + ws = bus_gvariant_determine_word_size(message_size, 0); + if (footer_accessible < ws) + return -EBADMSG; + + m->fields_size = bus_gvariant_read_word_le((uint8_t*) footer + footer_accessible - ws, ws); + if (ALIGN8(m->fields_size) > message_size - ws) + return -EBADMSG; + if (m->fields_size < sizeof(struct bus_header)) + return -EBADMSG; + + m->fields_size -= sizeof(struct bus_header); + m->body_size = message_size - (sizeof(struct bus_header) + ALIGN8(m->fields_size)); + } else { + if (h->dbus1.serial == 0) + return -EBADMSG; + + /* dbus1 has the sizes in the header */ + m->fields_size = BUS_MESSAGE_BSWAP32(m, h->dbus1.fields_size); + m->body_size = BUS_MESSAGE_BSWAP32(m, h->dbus1.body_size); + + if (sizeof(struct bus_header) + ALIGN8(m->fields_size) + m->body_size != message_size) + return -EBADMSG; + } + + m->fds = fds; + m->n_fds = n_fds; + + 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; + } + + m->bus = sd_bus_ref(bus); + *ret = m; + m = NULL; + + return 0; +} + +int bus_message_from_malloc( + sd_bus *bus, + void *buffer, + size_t length, + int *fds, + unsigned n_fds, + const char *label, + sd_bus_message **ret) { + + sd_bus_message *m; + size_t sz; + int r; + + r = bus_message_from_header( + bus, + buffer, length, /* in this case the initial bytes and the final bytes are the same */ + buffer, length, + length, + fds, n_fds, + label, + 0, &m); + if (r < 0) + return r; + + sz = length - sizeof(struct bus_header) - ALIGN8(m->fields_size); + if (sz > 0) { + m->n_body_parts = 1; + m->body.data = (uint8_t*) buffer + sizeof(struct bus_header) + ALIGN8(m->fields_size); + 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; + + assert(bus); + + 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->message_version; + m->allow_fds = bus->can_fds || (bus->state != BUS_HELLO && bus->state != BUS_RUNNING); + m->root_container.need_offsets = BUS_MESSAGE_IS_GVARIANT(m); + m->bus = sd_bus_ref(bus); + + if (bus->allow_interactive_authorization) + m->header->flags |= BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION; + + return m; +} + +_public_ int sd_bus_message_new_signal( + sd_bus *bus, + sd_bus_message **m, + const char *path, + const char *interface, + const char *member) { + + sd_bus_message *t; + int r; + + assert_return(bus, -ENOTCONN); + assert_return(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, + sd_bus_message **m, + const char *destination, + const char *path, + const char *interface, + const char *member) { + + sd_bus_message *t; + int r; + + assert_return(bus, -ENOTCONN); + assert_return(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->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); + if (t->reply_cookie == 0) + return -EOPNOTSUPP; + + r = message_append_reply_cookie(t, 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, + sd_bus_message **m, + const sd_bus_error *e) { + + 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_(sd_bus_error_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, m, &error); +} + +_public_ int sd_bus_message_new_method_errno( + sd_bus_message *call, + sd_bus_message **m, + int error, + const sd_bus_error *p) { + + _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; + + if (sd_bus_error_is_set(p)) + return sd_bus_message_new_method_error(call, m, p); + + sd_bus_error_set_errno(&berror, error); + + return sd_bus_message_new_method_error(call, m, &berror); +} + +_public_ int sd_bus_message_new_method_errnof( + sd_bus_message *call, + sd_bus_message **m, + int error, + const char *format, + ...) { + + _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; + va_list ap; + + va_start(ap, format); + sd_bus_error_set_errnofv(&berror, error, format, ap); + va_end(ap); + + return sd_bus_message_new_method_error(call, m, &berror); +} + +void bus_message_set_sender_local(sd_bus *bus, sd_bus_message *m) { + assert(bus); + assert(m); + + m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus.Local"; + m->creds.well_known_names_local = true; + m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; +} + +void bus_message_set_sender_driver(sd_bus *bus, sd_bus_message *m) { + assert(bus); + assert(m); + + m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus"; + m->creds.well_known_names_driver = true; + m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; +} + +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(bus); + 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_reply_cookie(t, 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; + + bus_message_set_sender_driver(bus, t); + + *m = t; + return 0; + +fail: + message_free(t); + return r; +} + +_public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) { + + if (!m) + return 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) + return NULL; + + 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) { + uint64_t c; + + assert_return(m, -EINVAL); + assert_return(cookie, -EINVAL); + + c = BUS_MESSAGE_COOKIE(m); + if (c == 0) + return -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); + + if (m->reply_cookie == 0) + return -ENODATA; + + *cookie = m->reply_cookie; + return 0; +} + +_public_ int sd_bus_message_get_expect_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); +} + +_public_ int sd_bus_message_get_auto_start(sd_bus_message *m) { + assert_return(m, -EINVAL); + + return !(m->header->flags & BUS_MESSAGE_NO_AUTO_START); +} + +_public_ int sd_bus_message_get_allow_interactive_authorization(sd_bus_message *m) { + assert_return(m, -EINVAL); + + return m->header->type == SD_BUS_MESSAGE_METHOD_CALL && + (m->header->flags & BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION); +} + +_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); + + if (!sd_bus_error_is_set(&m->error)) + return NULL; + + return &m->error; +} + +_public_ int sd_bus_message_get_monotonic_usec(sd_bus_message *m, uint64_t *usec) { + assert_return(m, -EINVAL); + assert_return(usec, -EINVAL); + + if (m->monotonic <= 0) + return -ENODATA; + + *usec = m->monotonic; + return 0; +} + +_public_ int sd_bus_message_get_realtime_usec(sd_bus_message *m, uint64_t *usec) { + assert_return(m, -EINVAL); + assert_return(usec, -EINVAL); + + if (m->realtime <= 0) + return -ENODATA; + + *usec = m->realtime; + return 0; +} + +_public_ int sd_bus_message_get_seqnum(sd_bus_message *m, uint64_t *seqnum) { + assert_return(m, -EINVAL); + assert_return(seqnum, -EINVAL); + + if (m->seqnum <= 0) + return -ENODATA; + + *seqnum = m->seqnum; + 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_expect_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); + + SET_FLAG(m->header->flags, BUS_MESSAGE_NO_REPLY_EXPECTED, !b); + + return 0; +} + +_public_ int sd_bus_message_set_auto_start(sd_bus_message *m, int b) { + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + SET_FLAG(m->header->flags, BUS_MESSAGE_NO_AUTO_START, !b); + + return 0; +} + +_public_ int sd_bus_message_set_allow_interactive_authorization(sd_bus_message *m, int b) { + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + SET_FLAG(m->header->flags, BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION, b); + + 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); + part->mmap_begin = part->data; + } + + 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 = memfd_set_size(part->memfd, new_allocated); + if (r < 0) { + m->poisoned = true; + return r; + } + + 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->mmap_begin, part->mapped, psz, MREMAP_MAYMOVE); + + if (n == MAP_FAILED) { + m->poisoned = true; + return -errno; + } + + part->mmap_begin = part->data = n; + part->mapped = psz; + part->memfd_offset = 0; + } + + 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, + bool force_inline) { + + 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->body_size, align); + end_body = start_body + sz; + + padding = start_body - m->body_size; + added = padding + sz; + + /* Check for 32bit overflows */ + if (end_body > (size_t) ((uint32_t) -1) || + end_body < start_body) { + 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) || + (force_inline && m->body_end->size > MEMFD_MIN_SIZE); /* if this must be an inlined extension, let's create a new part if the previous part is large enough to be inlined */ + + 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) { + memzero(p, 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->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 -EOPNOTSUPP; + + 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; + safe_close(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, false); + 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, 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, false); + if (!a) + return -ENOMEM; + + *s = a; + } else { + a = message_extend_body(m, 4, 4 + size + 1, false, 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, false)) + return -ENOMEM; + + r = bus_gvariant_is_fixed_size(contents); + if (r < 0) + return r; + + *begin = m->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, 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, 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, false)) + return -ENOMEM; + + } else { + size_t l; + void *a; + + l = strlen(contents); + a = message_extend_body(m, 1, 1 + l + 1, false, 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, false)) + return -ENOMEM; + + r = bus_gvariant_is_fixed_size(contents); + if (r < 0) + return r; + + *begin = m->body_size; + *need_offsets = r == 0; + } else { + /* Align contents to 8 byte boundary */ + if (!message_extend_body(m, 8, 0, false, 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, false)) + return -ENOMEM; + + r = bus_gvariant_is_fixed_size(contents); + if (r < 0) + return r; + + *begin = m->body_size; + *need_offsets = r == 0; + } else { + /* Align contents to 8 byte boundary */ + if (!message_extend_body(m, 8, 0, false, 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 = 0; + 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->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 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 = bus_gvariant_determine_word_size(payload, c->n_offsets); + + a = message_extend_body(m, 1, sz * c->n_offsets, true, false); + if (!a) + return -ENOMEM; + + for (i = 0; i < c->n_offsets; i++) + bus_gvariant_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, false); /* 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); + assert(c->signature); + + if (!BUS_MESSAGE_IS_GVARIANT(m)) + return 0; + + l = strlen(c->signature); + + a = message_extend_body(m, 1, 1 + l, true, false); + 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) { + bool fixed_size = true; + 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) + fixed_size = false; + 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 (isempty(c->signature)) { + /* The unary type is encoded as fixed 1 byte padding */ + a = message_extend_body(m, 1, 1, add_offset, false); + if (!a) + return -ENOMEM; + + *a = 0; + } else if (n_variable <= 0) { + int alignment = 1; + + /* Structures with fixed-size members only have to be + * fixed-size themselves. But gvariant requires all fixed-size + * elements to be sized a multiple of their alignment. Hence, + * we must *always* add final padding after the last member so + * the overall size of the structure is properly aligned. */ + if (fixed_size) + alignment = bus_gvariant_get_alignment(strempty(c->signature)); + + assert(alignment > 0); + + a = message_extend_body(m, alignment, 0, add_offset, false); + if (!a) + return -ENOMEM; + } else { + size_t sz; + unsigned j; + + assert(c->offsets[c->n_offsets-1] == m->body_size); + + sz = bus_gvariant_determine_word_size(m->body_size - c->begin, n_variable); + + a = message_extend_body(m, 1, sz * n_variable, add_offset, false); + 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; + + bus_gvariant_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: { + uint64_t x; + + x = va_arg(ap, uint64_t); + r = sd_bus_message_append_basic(m, *t, &x); + break; + } + + case SD_BUS_TYPE_DOUBLE: { + double x; + + x = va_arg(ap, double); + 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, 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; + + memcpy_safe(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 + memzero(p, 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, + int memfd, + uint64_t offset, + uint64_t size) { + + _cleanup_close_ int copy_fd = -1; + struct bus_body_part *part; + ssize_t align, sz; + uint64_t real_size; + void *a; + int r; + + assert_return(m, -EINVAL); + assert_return(memfd >= 0, -EBADF); + assert_return(bus_type_is_trivial(type), -EINVAL); + assert_return(size > 0, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(!m->poisoned, -ESTALE); + + r = memfd_set_sealed(memfd); + if (r < 0) + return r; + + copy_fd = dup(memfd); + if (copy_fd < 0) + return copy_fd; + + r = memfd_get_size(memfd, &real_size); + if (r < 0) + return r; + + if (offset == 0 && size == (uint64_t) -1) + size = real_size; + else if (offset + size > real_size) + return -EMSGSIZE; + + align = bus_type_get_alignment(type); + sz = bus_type_get_size(type); + + assert_se(align > 0); + assert_se(sz > 0); + + if (offset % align != 0) + return -EINVAL; + + 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, false); + if (!a) + return -ENOMEM; + + part = message_append_part(m); + if (!part) + return -ENOMEM; + + part->memfd = copy_fd; + part->memfd_offset = offset; + part->sealed = true; + part->size = size; + copy_fd = -1; + + m->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, + int memfd, + uint64_t offset, + uint64_t size) { + + _cleanup_close_ int copy_fd = -1; + struct bus_body_part *part; + struct bus_container *c; + uint64_t real_size; + void *a; + int r; + + assert_return(m, -EINVAL); + assert_return(memfd >= 0, -EBADF); + assert_return(size > 0, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(!m->poisoned, -ESTALE); + + r = memfd_set_sealed(memfd); + if (r < 0) + return r; + + copy_fd = dup(memfd); + if (copy_fd < 0) + return copy_fd; + + r = memfd_get_size(memfd, &real_size); + if (r < 0) + return r; + + if (offset == 0 && size == (uint64_t) -1) + size = real_size; + else if (offset + size > real_size) + return -EMSGSIZE; + + /* 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, false); + if (!a) + return -ENOMEM; + + *(uint32_t*) a = size - 1; + } + + part = message_append_part(m); + if (!part) + return -ENOMEM; + + part->memfd = copy_fd; + part->memfd_offset = offset; + part->sealed = true; + part->size = size; + copy_fd = -1; + + m->body_size += size; + message_extend_containers(m, size); + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + r = message_add_offset(m, m->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) { + + assert(m); + + /* The actual user data is finished now, we just complete the + variant and struct now (at least on gvariant). Remember + this position, so that during parsing we know where to to + put the outer container end. */ + m->user_body_size = m->body_size; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + const char *signature; + size_t sz, l; + void *d; + + /* Add offset table to end of fields array */ + if (m->n_header_offsets >= 1) { + uint8_t *a; + unsigned i; + + assert(m->fields_size == m->header_offsets[m->n_header_offsets-1]); + + sz = bus_gvariant_determine_word_size(m->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++) + bus_gvariant_write_word_le(a + sz*i, sz, m->header_offsets[i]); + } + + /* Add gvariant NUL byte plus signature to the end of + * the body, followed by the final offset pointing to + * the end of the fields array */ + + signature = strempty(m->root_container.signature); + l = strlen(signature); + + sz = bus_gvariant_determine_word_size(sizeof(struct bus_header) + ALIGN8(m->fields_size) + m->body_size + 1 + l + 2, 1); + d = message_extend_body(m, 1, 1 + l + 2 + sz, false, true); + if (!d) + return -ENOMEM; + + *(uint8_t*) d = 0; + *((uint8_t*) d + 1) = SD_BUS_TYPE_STRUCT_BEGIN; + memcpy((uint8_t*) d + 2, signature, l); + *((uint8_t*) d + 1 + l + 1) = SD_BUS_TYPE_STRUCT_END; + + bus_gvariant_write_word_le((uint8_t*) d + 1 + l + 2, sz, sizeof(struct bus_header) + m->fields_size); + + m->footer = d; + m->footer_accessible = 1 + l + 2 + sz; + } else { + m->header->dbus1.fields_size = m->fields_size; + m->header->dbus1.body_size = m->body_size; + } + + return 0; +} + +int bus_message_seal(sd_bus_message *m, uint64_t cookie, usec_t timeout) { + struct bus_body_part *part; + size_t a; + unsigned i; + int r; + + assert(m); + + if (m->sealed) + return -EPERM; + + if (m->n_containers > 0) + return -EBADMSG; + + if (m->poisoned) + return -ESTALE; + + if (cookie > 0xffffffffULL && + !BUS_MESSAGE_IS_GVARIANT(m)) + return -EOPNOTSUPP; + + /* 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, but only on dbus1 */ + if (!isempty(m->root_container.signature) && !BUS_MESSAGE_IS_GVARIANT(m)) { + 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; + + if (BUS_MESSAGE_IS_GVARIANT(m)) + m->header->dbus2.cookie = cookie; + else + m->header->dbus1.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. */ + a = ALIGN8(m->fields_size) - m->fields_size; + if (a > 0) + memzero((uint8_t*) BUS_MESSAGE_FIELDS(m) + m->fields_size, 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->use_memfd) { + MESSAGE_FOREACH_PART(part, i, m) + if (part->memfd >= 0 && + !part->sealed && + (part->size > MEMFD_MIN_SIZE || m->bus->use_memfd < 0) && + part != m->body_end) { /* The last part may never be sent as memfd */ + 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; + r = memfd_set_size(part->memfd, sz); + if (r < 0) + return r; + + /* Finally, try to seal */ + if (memfd_set_sealed(part->memfd) >= 0) + part->sealed = true; + } + } + + m->root_container.end = m->user_body_size; + 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, shift; + + 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; + } + + shift = part->memfd_offset - ((part->memfd_offset / page_size()) * page_size()); + psz = PAGE_ALIGN(part->size + shift); + + if (part->memfd >= 0) + p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE, part->memfd, part->memfd_offset - shift); + 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->mmap_begin = p; + part->data = (uint8_t*) p + shift; + 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->mmap_begin) + return; + + if (!part->munmap_this) + return; + + assert_se(munmap(part->mmap_begin, part->mapped) == 0); + + part->mmap_begin = NULL; + 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 > m->user_body_size) + 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 { + + 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 = bus_gvariant_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 = bus_gvariant_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 = bus_gvariant_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)) { + /* Unary type is encoded as *fixed* 1 byte padding */ + r = message_peek_body(m, &m->rindex, 1, 1, &q); + if (r < 0) + return r; + + if (*(uint8_t *) q != 0) + return -EBADMSG; + + *item_size = 0; + *offsets = NULL; + *n_offsets = 0; + return 0; + } + + sz = bus_gvariant_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 = bus_gvariant_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 + /* 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; + + /* Unary type has fixed size of 1, but virtual size of 0 */ + if (BUS_MESSAGE_IS_GVARIANT(m) && + type == SD_BUS_TYPE_STRUCT && + isempty(signature)) + w->end = m->rindex + 0; + else + 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; /* length 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); + + /* If types is NULL, read exactly one element */ + if (!types) { + struct bus_container *c; + size_t l; + + if (message_end_of_signature(m)) + return -ENXIO; + + if (message_end_of_array(m, m->rindex)) + return 0; + + c = message_get_container(m); + + r = signature_element_length(c->signature + c->index, &l); + if (r < 0) + return r; + + types = strndupa(c->signature + c->index, l); + } + + switch (*types) { + + case 0: /* Nothing to drop */ + 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_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; + + 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), -EOPNOTSUPP); + + 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), m->fields_size, 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_uint64( + sd_bus_message *m, + size_t *ri, + size_t item_size, + uint64_t *ret) { + + int r; + void *q; + + assert(m); + assert(ri); + + if (BUS_MESSAGE_IS_GVARIANT(m) && item_size != 8) + return -EBADMSG; + + /* identical for gvariant and dbus1 */ + + r = message_peek_fields(m, ri, 8, 8, &q); + if (r < 0) + return r; + + if (ret) + *ret = BUS_MESSAGE_BSWAP64(m, *(uint64_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; + bool unix_fds_set = false; + void *offsets = NULL; + unsigned n_offsets = 0; + size_t sz = 0; + unsigned i = 0; + + assert(m); + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + char *p; + + /* Read the signature from the end of the body variant first */ + sz = bus_gvariant_determine_word_size(BUS_MESSAGE_SIZE(m), 0); + if (m->footer_accessible < 1 + sz) + return -EBADMSG; + + p = (char*) m->footer + m->footer_accessible - (1 + sz); + for (;;) { + if (p < (char*) m->footer) + return -EBADMSG; + + if (*p == 0) { + size_t l; + char *c; + + /* We found the beginning of the signature + * string, yay! We require the body to be a + * structure, so verify it and then strip the + * opening/closing brackets. */ + + l = ((char*) m->footer + m->footer_accessible) - p - (1 + sz); + if (l < 2 || + p[1] != SD_BUS_TYPE_STRUCT_BEGIN || + p[1 + l - 1] != SD_BUS_TYPE_STRUCT_END) + return -EBADMSG; + + c = strndup(p + 1 + 1, l - 2); + if (!c) + return -ENOMEM; + + free(m->root_container.signature); + m->root_container.signature = c; + break; + } + + p--; + } + + /* Calculate the actual user body size, by removing + * the trailing variant signature and struct offset + * table */ + m->user_body_size = m->body_size - ((char*) m->footer + m->footer_accessible - p); + + /* Pull out the offset table for the fields array */ + sz = bus_gvariant_determine_word_size(m->fields_size, 0); + if (sz > 0) { + size_t framing; + void *q; + + ri = m->fields_size - sz; + r = message_peek_fields(m, &ri, 1, sz, &q); + if (r < 0) + return r; + + framing = bus_gvariant_read_word_le(q, sz); + if (framing >= m->fields_size - sz) + return -EBADMSG; + if ((m->fields_size - framing) % sz != 0) + return -EBADMSG; + + ri = framing; + r = message_peek_fields(m, &ri, 1, m->fields_size - framing, &offsets); + if (r < 0) + return r; + + n_offsets = (m->fields_size - framing) / sz; + } + } else + m->user_body_size = m->body_size; + + ri = 0; + while (ri < m->fields_size) { + _cleanup_free_ char *sig = NULL; + const char *signature; + uint64_t field_type; + size_t item_size = (size_t) -1; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + uint64_t *u64; + + if (i >= n_offsets) + break; + + if (i == 0) + ri = 0; + else + ri = ALIGN_TO(bus_gvariant_read_word_le((uint8_t*) offsets + (i-1)*sz, sz), 8); + + r = message_peek_fields(m, &ri, 8, 8, (void**) &u64); + if (r < 0) + return r; + + field_type = BUS_MESSAGE_BSWAP64(m, *u64); + } else { + uint8_t *u8; + + r = message_peek_fields(m, &ri, 8, 1, (void**) &u8); + if (r < 0) + return r; + + field_type = *u8; + } + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + size_t where, end; + char *b; + void *q; + + end = bus_gvariant_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 (field_type) { + + 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->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 (BUS_MESSAGE_IS_GVARIANT(m)) /* only applies to dbus1 */ + return -EBADMSG; + + 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 (BUS_MESSAGE_IS_GVARIANT(m)) { + /* 64bit on dbus2 */ + + if (!streq(signature, "t")) + return -EBADMSG; + + r = message_peek_field_uint64(m, &ri, item_size, &m->reply_cookie); + if (r < 0) + return r; + } else { + /* 32bit on dbus1 */ + uint32_t serial; + + if (!streq(signature, "u")) + return -EBADMSG; + + r = message_peek_field_uint32(m, &ri, item_size, &serial); + if (r < 0) + return r; + + m->reply_cookie = serial; + } + + if (m->reply_cookie == 0) + return -EBADMSG; + + break; + + case BUS_MESSAGE_HEADER_UNIX_FDS: + if (unix_fds_set) + return -EBADMSG; + + if (!streq(signature, "u")) + return -EBADMSG; + + r = message_peek_field_uint32(m, &ri, item_size, &unix_fds); + if (r < 0) + return -EBADMSG; + + unix_fds_set = true; + 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; + + if (m->reply_cookie != 0) + return -EBADMSG; + + break; + + case SD_BUS_MESSAGE_METHOD_CALL: + + if (!m->path || !m->member) + return -EBADMSG; + + if (m->reply_cookie != 0) + 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 = m->user_body_size; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + r = build_struct_offsets( + m, + m->root_container.signature, + m->user_body_size, + &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) + (void) 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) { + const char *s; + int r; + + assert(m); + assert(l); + + r = sd_bus_message_enter_container(m, 'a', "s"); + if (r <= 0) + return r; + + while ((r = sd_bus_message_read_basic(m, 's', &s)) > 0) { + r = strv_extend(l, s); + if (r < 0) + return r; + } + 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; +} + +static int bus_message_get_arg_skip( + sd_bus_message *m, + unsigned i, + char *_type, + const char **_contents) { + + unsigned j; + int r; + + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + for (j = 0;; j++) { + const char *contents; + char type; + + r = sd_bus_message_peek_type(m, &type, &contents); + if (r < 0) + return r; + if (r == 0) + return -ENXIO; + + /* Don't match against arguments after the first one we don't understand */ + if (!IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE) && + !(type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g"))) + return -ENXIO; + + if (j >= i) { + if (_contents) + *_contents = contents; + if (_type) + *_type = type; + return 0; + } + + r = sd_bus_message_skip(m, NULL); + if (r < 0) + return r; + } + +} + +int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str) { + char type; + int r; + + assert(m); + assert(str); + + r = bus_message_get_arg_skip(m, i, &type, NULL); + if (r < 0) + return r; + + if (!IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) + return -ENXIO; + + return sd_bus_message_read_basic(m, type, str); +} + +int bus_message_get_arg_strv(sd_bus_message *m, unsigned i, char ***strv) { + const char *contents; + char type; + int r; + + assert(m); + assert(strv); + + r = bus_message_get_arg_skip(m, i, &type, &contents); + if (r < 0) + return r; + + if (type != SD_BUS_TYPE_ARRAY) + return -ENXIO; + if (!STR_IN_SET(contents, "s", "o", "g")) + return -ENXIO; + + return sd_bus_message_read_strv(m, strv); +} + +_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_is_empty(sd_bus_message *m) { + assert_return(m, -EINVAL); + + return isempty(m->root_container.signature); +} + +_public_ int sd_bus_message_has_signature(sd_bus_message *m, const char *signature) { + assert_return(m, -EINVAL); + + return streq(strempty(m->root_container.signature), strempty(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_(sd_bus_message_unrefp) 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, &n, (*m)->path, (*m)->interface, (*m)->member); + if (r < 0) + return r; + + break; + + case SD_BUS_MESSAGE_METHOD_CALL: + r = sd_bus_message_new_method_call(bus, &n, (*m)->destination, (*m)->path, (*m)->interface, (*m)->member); + 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_reply_cookie(n, 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); +} + +_public_ int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority) { + assert_return(m, -EINVAL); + assert_return(priority, -EINVAL); + + *priority = m->priority; + return 0; +} + +_public_ int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority) { + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + m->priority = priority; + return 0; +} diff --git a/src/libsystemd/src/sd-bus/bus-message.h b/src/libsystemd/src/sd-bus/bus-message.h new file mode 100644 index 0000000000..6a2c2d533c --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-message.h @@ -0,0 +1,244 @@ +#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 . +***/ + +#include +#include +#include + +#include + +#include "bus-creds.h" +#include "bus-protocol.h" +#include "macro.h" +#include "time-util.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_body_part { + struct bus_body_part *next; + void *data; + void *mmap_begin; + size_t size; + size_t mapped; + size_t allocated; + uint64_t memfd_offset; + 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; + + uint64_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; + uint64_t seqnum; + int64_t priority; + uint64_t verify_destination_id; + + 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; + + /* The first and last bytes of the message */ + struct bus_header *header; + void *footer; + + /* How many bytes are accessible in the above pointers */ + size_t header_accessible; + size_t footer_accessible; + + size_t fields_size; + size_t body_size; + size_t user_body_size; + + 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; + size_t 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]; + char *destination_ptr; + + size_t header_offsets[_BUS_MESSAGE_HEADER_MAX]; + unsigned n_header_offsets; +}; + +static inline bool BUS_MESSAGE_NEED_BSWAP(sd_bus_message *m) { + return 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 uint64_t BUS_MESSAGE_COOKIE(sd_bus_message *m) { + if (m->header->version == 2) + return BUS_MESSAGE_BSWAP64(m, m->header->dbus2.cookie); + + return BUS_MESSAGE_BSWAP32(m, m->header->dbus1.serial); +} + +static inline size_t BUS_MESSAGE_SIZE(sd_bus_message *m) { + return + sizeof(struct bus_header) + + ALIGN8(m->fields_size) + + m->body_size; +} + +static inline size_t BUS_MESSAGE_BODY_BEGIN(sd_bus_message *m) { + return + sizeof(struct bus_header) + + ALIGN8(m->fields_size); +} + +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 header_accessible, + void *footer, + size_t footer_accessible, + size_t message_size, + int *fds, + unsigned n_fds, + 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 char *label, + sd_bus_message **ret); + +int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str); +int bus_message_get_arg_strv(sd_bus_message *m, unsigned i, char ***strv); + +int bus_message_append_ap(sd_bus_message *m, const char *types, va_list ap); + +int bus_message_parse_fields(sd_bus_message *m); + +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); + +void bus_message_set_sender_driver(sd_bus *bus, sd_bus_message *m); +void bus_message_set_sender_local(sd_bus *bus, sd_bus_message *m); diff --git a/src/libsystemd/src/sd-bus/bus-objects.c b/src/libsystemd/src/sd-bus/bus-objects.c new file mode 100644 index 0000000000..9bd07ffcab --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-objects.c @@ -0,0 +1,2806 @@ +/*** + 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 . +***/ + +#include "alloc-util.h" +#include "bus-internal.h" +#include "bus-introspect.h" +#include "bus-message.h" +#include "bus-objects.h" +#include "bus-signature.h" +#include "bus-slot.h" +#include "bus-type.h" +#include "bus-util.h" +#include "set.h" +#include "string-util.h" +#include "strv.h" + +static int node_vtable_get_userdata( + sd_bus *bus, + const char *path, + struct node_vtable *c, + void **userdata, + sd_bus_error *error) { + + sd_bus_slot *s; + void *u; + int r; + + assert(bus); + assert(path); + assert(c); + + s = container_of(c, sd_bus_slot, node_vtable); + u = s->userdata; + if (c->find) { + bus->current_slot = sd_bus_slot_ref(s); + bus->current_userdata = u; + r = c->find(bus, path, c->interface, u, &u, error); + bus->current_userdata = NULL; + bus->current_slot = sd_bus_slot_unref(s); + + 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_method_convert_userdata(const sd_bus_vtable *p, void *u) { + assert(p); + + return (uint8_t*) u + p->x.method.offset; +} + +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; + sd_bus_slot *slot; + + if (bus->nodes_modified) + return 0; + + slot = container_of(c, sd_bus_slot, node_enumerator); + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_userdata = slot->userdata; + r = c->callback(bus, prefix, slot->userdata, &children, error); + bus->current_userdata = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + + 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; +} + +enum { + /* if set, add_subtree() works recursively */ + CHILDREN_RECURSIVE = (1U << 1), + /* if set, add_subtree() scans object-manager hierarchies recursively */ + CHILDREN_SUBHIERARCHIES = (1U << 0), +}; + +static int add_subtree_to_set( + sd_bus *bus, + const char *prefix, + struct node *n, + unsigned int flags, + 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; + + if ((flags & CHILDREN_RECURSIVE) && + ((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) { + r = add_subtree_to_set(bus, prefix, i, flags, 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, + unsigned int flags, + Set **_s, + sd_bus_error *error) { + + Set *s = NULL; + int r; + + assert(bus); + assert(prefix); + assert(n); + assert(_s); + + s = set_new(&string_hash_ops); + if (!s) + return -ENOMEM; + + r = add_subtree_to_set(bus, prefix, n, flags, 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_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; + sd_bus_slot *slot; + + 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; + + slot = container_of(c, sd_bus_slot, node_callback); + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = c->callback; + bus->current_userdata = slot->userdata; + r = c->callback(m, slot->userdata, &error_buffer); + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + + 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) { + uint64_t cap; + 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; + + /* 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_query_sender_privilege(m, cap); + if (r < 0) + return r; + if (r > 0) + return 0; + + 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_(sd_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; + + u = vtable_method_convert_userdata(c->vtable, u); + + *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) { + sd_bus_slot *slot; + + slot = container_of(c->parent, sd_bus_slot, node_vtable); + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = c->vtable->x.method.handler; + bus->current_userdata = u; + r = c->vtable->x.method.handler(m, u, &error); + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + + 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, + sd_bus_slot *slot, + 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(slot); + assert(v); + assert(path); + assert(interface); + assert(property); + assert(reply); + + if (v->x.property.get) { + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_userdata = userdata; + r = v->x.property.get(bus, path, interface, property, reply, userdata, error); + bus->current_userdata = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + + 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, + sd_bus_slot *slot, + 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(slot); + assert(v); + assert(path); + assert(interface); + assert(property); + assert(value); + + if (v->x.property.set) { + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_userdata = userdata; + r = v->x.property.set(bus, path, interface, property, value, userdata, error); + bus->current_userdata = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + + 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + sd_bus_slot *slot; + void *u = NULL; + 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; + + slot = container_of(c->parent, sd_bus_slot, node_vtable); + + *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, slot, 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 { + const char *signature = NULL; + char type = 0; + + 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_peek_type(m, &type, &signature); + if (r < 0) + return r; + + if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature))) + return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Incorrect parameters for property '%s', expected '%s', got '%s'.", c->member, strempty(c->vtable->x.property.signature), strempty(signature)); + + 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, slot, 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) { + + sd_bus_slot *slot; + 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; + + slot = container_of(c, sd_bus_slot, node_vtable); + + r = invoke_property_get(bus, slot, 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; + + if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT) + 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_(sd_bus_message_unrefp) 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_(sd_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 int bus_node_exists( + sd_bus *bus, + struct node *n, + const char *path, + bool require_fallback) { + + struct node_vtable *c; + struct node_callback *k; + int r; + + assert(bus); + assert(n); + assert(path); + + /* Tests if there's anything attached directly to this node + * for the specified path */ + + if (!require_fallback && (n->enumerators || n->object_managers)) + return true; + + LIST_FOREACH(callbacks, k, n->callbacks) { + if (require_fallback && !k->is_fallback) + continue; + + return 1; + } + + LIST_FOREACH(vtables, c, n->vtables) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + + if (require_fallback && !c->is_fallback) + continue; + + r = node_vtable_get_userdata(bus, path, c, NULL, &error); + if (r != 0) + return r; + if (bus->nodes_modified) + return 0; + } + + return 0; +} + +static int process_introspect( + sd_bus *bus, + sd_bus_message *m, + struct node *n, + bool require_fallback, + bool *found_object) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) 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, 0, &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, !require_fallback && n->object_managers); + 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(" \n", intro.f); + + fprintf(intro.f, " \n", c->interface); + } + + r = introspect_write_interface(&intro, c->vtable); + if (r < 0) + goto finish; + + previous_interface = c->interface; + } + + if (previous_interface) + fputs(" \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) + goto finish; + if (bus->nodes_modified) { + 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_set_free_free_ Set *s = NULL; + Iterator i; + char *path; + int r; + + assert(bus); + assert(m); + assert(n); + assert(found_object); + + /* Spec says, GetManagedObjects() is only implemented on the root of a + * sub-tree. Therefore, we require a registered object-manager on + * exactly the queried path, otherwise, we refuse to respond. */ + + if (require_fallback || !n->object_managers) + return 0; + + r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &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; + + 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 (bus->nodes_modified) + return 0; + 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 (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + + 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_ops); + 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, e - path)); + + 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; +} + +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_managers) + 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_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) { + struct node *n; + + assert(bus); + assert(path); + + n = hashmap_get(bus->nodes, path); + if (!n) { + char *prefix; + + prefix = alloca(strlen(path) + 1); + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { + n = hashmap_get(bus->nodes, prefix); + if (n) + break; + } + } + + while (n && !n->object_managers) + n = n->parent; + + if (out) + *out = n; + return !!n; +} + +static int bus_add_object( + sd_bus *bus, + sd_bus_slot **slot, + bool fallback, + const char *path, + sd_bus_message_handler_t callback, + void *userdata) { + + sd_bus_slot *s; + 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; + + s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata); + if (!s) { + r = -ENOMEM; + goto fail; + } + + s->node_callback.callback = callback; + s->node_callback.is_fallback = fallback; + + s->node_callback.node = n; + LIST_PREPEND(callbacks, n->callbacks, &s->node_callback); + bus->nodes_modified = true; + + if (slot) + *slot = s; + + return 0; + +fail: + sd_bus_slot_unref(s); + bus_node_gc(bus, n); + + return r; +} + +_public_ int sd_bus_add_object( + sd_bus *bus, + sd_bus_slot **slot, + const char *path, + sd_bus_message_handler_t callback, + void *userdata) { + + return bus_add_object(bus, slot, false, path, callback, userdata); +} + +_public_ int sd_bus_add_fallback( + sd_bus *bus, + sd_bus_slot **slot, + const char *prefix, + sd_bus_message_handler_t callback, + void *userdata) { + + return bus_add_object(bus, slot, true, prefix, callback, userdata); +} + +static void vtable_member_hash_func(const void *a, struct siphash *state) { + const struct vtable_member *m = a; + + assert(m); + + string_hash_func(m->path, state); + string_hash_func(m->interface, state); + string_hash_func(m->member, state); +} + +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 const struct hash_ops vtable_member_hash_ops = { + .hash = vtable_member_hash_func, + .compare = vtable_member_compare_func +}; + +static int add_object_vtable_internal( + sd_bus *bus, + sd_bus_slot **slot, + const char *path, + const char *interface, + const sd_bus_vtable *vtable, + bool fallback, + sd_bus_object_find_t find, + void *userdata) { + + sd_bus_slot *s = NULL; + struct node_vtable *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_ops); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops); + 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; + } + } + + s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata); + if (!s) { + r = -ENOMEM; + goto fail; + } + + s->node_vtable.is_fallback = fallback; + s->node_vtable.vtable = vtable; + s->node_vtable.find = find; + + s->node_vtable.interface = strdup(interface); + if (!s->node_vtable.interface) { + r = -ENOMEM; + goto fail; + } + + for (v = s->node_vtable.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 = &s->node_vtable; + m->path = n->path; + m->interface = s->node_vtable.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; + } + + if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) { + 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_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) || + (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 = &s->node_vtable; + m->path = n->path; + m->interface = s->node_vtable.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; + } + } + + s->node_vtable.node = n; + LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable); + bus->nodes_modified = true; + + if (slot) + *slot = s; + + return 0; + +fail: + sd_bus_slot_unref(s); + bus_node_gc(bus, n); + + return r; +} + +_public_ int sd_bus_add_object_vtable( + sd_bus *bus, + sd_bus_slot **slot, + const char *path, + const char *interface, + const sd_bus_vtable *vtable, + void *userdata) { + + return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata); +} + +_public_ int sd_bus_add_fallback_vtable( + sd_bus *bus, + sd_bus_slot **slot, + const char *prefix, + const char *interface, + const sd_bus_vtable *vtable, + sd_bus_object_find_t find, + void *userdata) { + + return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata); +} + +_public_ int sd_bus_add_node_enumerator( + sd_bus *bus, + sd_bus_slot **slot, + const char *path, + sd_bus_node_enumerator_t callback, + void *userdata) { + + sd_bus_slot *s; + 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; + + s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata); + if (!s) { + r = -ENOMEM; + goto fail; + } + + s->node_enumerator.callback = callback; + + s->node_enumerator.node = n; + LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator); + bus->nodes_modified = true; + + if (slot) + *slot = s; + + return 0; + +fail: + sd_bus_slot_unref(s); + bus_node_gc(bus, n); + + return r; +} + +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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) 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, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged"); + 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_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + /* 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_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (!name) + return 0; + + names = strv_from_stdarg_alloca(name); + + return sd_bus_emit_properties_changed_strv(bus, path, interface, names); +} + +static int object_added_append_all_prefix( + sd_bus *bus, + sd_bus_message *m, + Set *s, + const char *prefix, + const char *path, + bool require_fallback) { + + const char *previous_interface = NULL; + struct node_vtable *c; + struct node *n; + int r; + + assert(bus); + assert(m); + assert(s); + assert(prefix); + assert(path); + + n = hashmap_get(bus->nodes, prefix); + if (!n) + return 0; + + LIST_FOREACH(vtables, c, n->vtables) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + void *u = NULL; + + if (require_fallback && !c->is_fallback) + 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 (!streq_ptr(c->interface, previous_interface)) { + /* If a child-node already handled this interface, we + * skip it on any of its parents. The child vtables + * always fully override any conflicting vtables of + * any parent node. */ + if (set_get(s, c->interface)) + continue; + + r = set_put(s, c->interface); + if (r < 0) + return r; + + if (previous_interface) { + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + } + + r = sd_bus_message_open_container(m, 'e', "sa{sv}"); + if (r < 0) + return r; + r = sd_bus_message_append(m, "s", c->interface); + if (r < 0) + return r; + r = sd_bus_message_open_container(m, 'a', "{sv}"); + if (r < 0) + return r; + + previous_interface = c->interface; + } + + r = vtable_append_all_properties(bus, m, path, c, u, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } + + if (previous_interface) { + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + } + + return 0; +} + +static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { + _cleanup_set_free_ Set *s = NULL; + char *prefix; + int r; + + assert(bus); + assert(m); + assert(path); + + /* + * This appends all interfaces registered on path @path. We first add + * the builtin interfaces, which are always available and handled by + * sd-bus. Then, we add all interfaces registered on the exact node, + * followed by all fallback interfaces registered on any parent prefix. + * + * If an interface is registered multiple times on the same node with + * different vtables, we merge all the properties across all vtables. + * However, if a child node has the same interface registered as one of + * its parent nodes has as fallback, we make the child overwrite the + * parent instead of extending it. Therefore, we keep a "Set" of all + * handled interfaces during parent traversal, so we skip interfaces on + * a parent that were overwritten by a child. + */ + + s = set_new(&string_hash_ops); + if (!s) + return -ENOMEM; + + r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0); + if (r < 0) + return r; + r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0); + if (r < 0) + return r; + r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0); + if (r < 0) + return r; + r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0); + if (r < 0) + return r; + + r = object_added_append_all_prefix(bus, m, s, path, path, false); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + + prefix = alloca(strlen(path) + 1); + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { + r = object_added_append_all_prefix(bus, m, s, prefix, path, true); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } + + return 0; +} + +_public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) { + BUS_DONT_DESTROY(bus); + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct node *object_manager; + int r; + + /* + * This emits an InterfacesAdded signal on the given path, by iterating + * all registered vtables and fallback vtables on the path. All + * properties are queried and included in the signal. + * This call is equivalent to sd_bus_emit_interfaces_added() with an + * explicit list of registered interfaces. However, unlike + * interfaces_added(), this call can figure out the list of supported + * interfaces itself. Furthermore, it properly adds the builtin + * org.freedesktop.DBus.* interfaces. + */ + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + r = bus_find_parent_object_manager(bus, &object_manager, path); + if (r < 0) + return r; + if (r == 0) + return -ESRCH; + + do { + bus->nodes_modified = false; + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"); + 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; + + r = object_added_append_all(bus, m, path); + 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); +} + +static int object_removed_append_all_prefix( + sd_bus *bus, + sd_bus_message *m, + Set *s, + const char *prefix, + const char *path, + bool require_fallback) { + + const char *previous_interface = NULL; + struct node_vtable *c; + struct node *n; + int r; + + assert(bus); + assert(m); + assert(s); + assert(prefix); + assert(path); + + n = hashmap_get(bus->nodes, prefix); + if (!n) + return 0; + + LIST_FOREACH(vtables, c, n->vtables) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + void *u = NULL; + + if (require_fallback && !c->is_fallback) + continue; + if (streq_ptr(c->interface, previous_interface)) + continue; + + /* If a child-node already handled this interface, we + * skip it on any of its parents. The child vtables + * always fully override any conflicting vtables of + * any parent node. */ + if (set_get(s, c->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; + + r = set_put(s, c->interface); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "s", c->interface); + if (r < 0) + return r; + + previous_interface = c->interface; + } + + return 0; +} + +static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { + _cleanup_set_free_ Set *s = NULL; + char *prefix; + int r; + + assert(bus); + assert(m); + assert(path); + + /* see sd_bus_emit_object_added() for details */ + + s = set_new(&string_hash_ops); + if (!s) + return -ENOMEM; + + r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer"); + if (r < 0) + return r; + r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable"); + if (r < 0) + return r; + r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties"); + if (r < 0) + return r; + r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager"); + if (r < 0) + return r; + + r = object_removed_append_all_prefix(bus, m, s, path, path, false); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + + prefix = alloca(strlen(path) + 1); + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { + r = object_removed_append_all_prefix(bus, m, s, prefix, path, true); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } + + return 0; +} + +_public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) { + BUS_DONT_DESTROY(bus); + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct node *object_manager; + int r; + + /* + * This is like sd_bus_emit_object_added(), but emits an + * InterfacesRemoved signal on the given path. This only includes any + * registered interfaces but skips the properties. Note that this will + * call into the find() callbacks of any registered vtable. Therefore, + * you must call this function before destroying/unlinking your object. + * Otherwise, the list of interfaces will be incomplete. However, note + * that this will *NOT* call into any property callback. Therefore, the + * object might be in an "destructed" state, as long as we can find it. + */ + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + r = bus_find_parent_object_manager(bus, &object_manager, path); + if (r < 0) + return r; + if (r == 0) + return -ESRCH; + + do { + bus->nodes_modified = false; + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"); + 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', "s"); + if (r < 0) + return r; + + r = object_removed_append_all(bus, m, path); + 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); +} + +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_(sd_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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct node *object_manager; + char **i; + int r; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (strv_isempty(interfaces)) + return 0; + + r = bus_find_parent_object_manager(bus, &object_manager, path); + if (r < 0) + return r; + if (r == 0) + return -ESRCH; + + do { + bus->nodes_modified = false; + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"); + 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_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + 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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct node *object_manager; + int r; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (strv_isempty(interfaces)) + return 0; + + r = bus_find_parent_object_manager(bus, &object_manager, path); + if (r < 0) + return r; + if (r == 0) + return -ESRCH; + + r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"); + 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_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + 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, sd_bus_slot **slot, const char *path) { + sd_bus_slot *s; + struct node *n; + int r; + + 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; + + s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL); + if (!s) { + r = -ENOMEM; + goto fail; + } + + s->node_object_manager.node = n; + LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager); + bus->nodes_modified = true; + + if (slot) + *slot = s; + + return 0; + +fail: + sd_bus_slot_unref(s); + bus_node_gc(bus, n); + + return r; +} diff --git a/src/libsystemd/src/sd-bus/bus-objects.h b/src/libsystemd/src/sd-bus/bus-objects.h new file mode 100644 index 0000000000..e0b8c534ed --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-objects.h @@ -0,0 +1,25 @@ +#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 . +***/ + +#include "bus-internal.h" + +int bus_process_object(sd_bus *bus, sd_bus_message *m); +void bus_node_gc(sd_bus *b, struct node *n); diff --git a/src/libsystemd/src/sd-bus/bus-protocol.h b/src/libsystemd/src/sd-bus/bus-protocol.h new file mode 100644 index 0000000000..9d180cb284 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-protocol.h @@ -0,0 +1,180 @@ +#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 . +***/ + +#include + +#include "macro.h" + +/* Packet header */ + +struct _packed_ bus_header { + /* The first four fields are identical for dbus1, and dbus2 */ + uint8_t endian; + uint8_t type; + uint8_t flags; + uint8_t version; + + union _packed_ { + /* dbus1: Used for SOCK_STREAM connections */ + struct _packed_ { + 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; + } dbus1; + + /* dbus2: Used for kdbus connections */ + struct _packed_ { + uint32_t _reserved; + uint64_t cookie; + } dbus2; + + /* Note that both header versions have the same size! */ + }; +}; + +/* 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, + BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION = 4, +}; + +/* 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 \ + "\n" + +#define BUS_INTROSPECT_INTERFACE_PEER \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define BUS_INTROSPECT_INTERFACE_PROPERTIES \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" diff --git a/src/libsystemd/src/sd-bus/bus-signature.c b/src/libsystemd/src/sd-bus/bus-signature.c new file mode 100644 index 0000000000..7bc243494a --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-signature.c @@ -0,0 +1,158 @@ +/*** + 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 . +***/ + +#include + +#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/src/sd-bus/bus-signature.h b/src/libsystemd/src/sd-bus/bus-signature.h new file mode 100644 index 0000000000..1e0cd7f587 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-signature.h @@ -0,0 +1,28 @@ +#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 . +***/ + +#include + +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/src/sd-bus/bus-slot.c b/src/libsystemd/src/sd-bus/bus-slot.c new file mode 100644 index 0000000000..75c1692bf5 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-slot.c @@ -0,0 +1,286 @@ +/*** + 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 . +***/ + +#include + +#include "alloc-util.h" +#include "bus-control.h" +#include "bus-objects.h" +#include "bus-slot.h" +#include "string-util.h" + +sd_bus_slot *bus_slot_allocate( + sd_bus *bus, + bool floating, + BusSlotType type, + size_t extra, + void *userdata) { + + sd_bus_slot *slot; + + assert(bus); + + slot = malloc0(offsetof(sd_bus_slot, reply_callback) + extra); + if (!slot) + return NULL; + + slot->n_ref = 1; + slot->type = type; + slot->bus = bus; + slot->floating = floating; + slot->userdata = userdata; + + if (!floating) + sd_bus_ref(bus); + + LIST_PREPEND(slots, bus->slots, slot); + + return slot; +} + +_public_ sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot) { + + if (!slot) + return NULL; + + assert(slot->n_ref > 0); + + slot->n_ref++; + return slot; +} + +void bus_slot_disconnect(sd_bus_slot *slot) { + sd_bus *bus; + + assert(slot); + + if (!slot->bus) + return; + + switch (slot->type) { + + case BUS_REPLY_CALLBACK: + + if (slot->reply_callback.cookie != 0) + ordered_hashmap_remove(slot->bus->reply_callbacks, &slot->reply_callback.cookie); + + if (slot->reply_callback.timeout != 0) + prioq_remove(slot->bus->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx); + + break; + + case BUS_FILTER_CALLBACK: + slot->bus->filter_callbacks_modified = true; + LIST_REMOVE(callbacks, slot->bus->filter_callbacks, &slot->filter_callback); + break; + + case BUS_MATCH_CALLBACK: + + if (slot->match_added) + bus_remove_match_internal(slot->bus, slot->match_callback.match_string, slot->match_callback.cookie); + + slot->bus->match_callbacks_modified = true; + bus_match_remove(&slot->bus->match_callbacks, &slot->match_callback); + + free(slot->match_callback.match_string); + + break; + + case BUS_NODE_CALLBACK: + + if (slot->node_callback.node) { + LIST_REMOVE(callbacks, slot->node_callback.node->callbacks, &slot->node_callback); + slot->bus->nodes_modified = true; + + bus_node_gc(slot->bus, slot->node_callback.node); + } + + break; + + case BUS_NODE_ENUMERATOR: + + if (slot->node_enumerator.node) { + LIST_REMOVE(enumerators, slot->node_enumerator.node->enumerators, &slot->node_enumerator); + slot->bus->nodes_modified = true; + + bus_node_gc(slot->bus, slot->node_enumerator.node); + } + + break; + + case BUS_NODE_OBJECT_MANAGER: + + if (slot->node_object_manager.node) { + LIST_REMOVE(object_managers, slot->node_object_manager.node->object_managers, &slot->node_object_manager); + slot->bus->nodes_modified = true; + + bus_node_gc(slot->bus, slot->node_object_manager.node); + } + + break; + + case BUS_NODE_VTABLE: + + if (slot->node_vtable.node && slot->node_vtable.interface && slot->node_vtable.vtable) { + const sd_bus_vtable *v; + + for (v = slot->node_vtable.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 = slot->node_vtable.node->path; + key.interface = slot->node_vtable.interface; + key.member = v->x.method.member; + + x = hashmap_remove(slot->bus->vtable_methods, &key); + break; + } + + case _SD_BUS_VTABLE_PROPERTY: + case _SD_BUS_VTABLE_WRITABLE_PROPERTY: { + struct vtable_member key; + + key.path = slot->node_vtable.node->path; + key.interface = slot->node_vtable.interface; + key.member = v->x.method.member; + + + x = hashmap_remove(slot->bus->vtable_properties, &key); + break; + }} + + free(x); + } + } + + free(slot->node_vtable.interface); + + if (slot->node_vtable.node) { + LIST_REMOVE(vtables, slot->node_vtable.node->vtables, &slot->node_vtable); + slot->bus->nodes_modified = true; + + bus_node_gc(slot->bus, slot->node_vtable.node); + } + + break; + + default: + assert_not_reached("Wut? Unknown slot type?"); + } + + bus = slot->bus; + + slot->type = _BUS_SLOT_INVALID; + slot->bus = NULL; + LIST_REMOVE(slots, bus->slots, slot); + + if (!slot->floating) + sd_bus_unref(bus); +} + +_public_ sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) { + + if (!slot) + return NULL; + + assert(slot->n_ref > 0); + + if (slot->n_ref > 1) { + slot->n_ref--; + return NULL; + } + + bus_slot_disconnect(slot); + free(slot->description); + free(slot); + + return NULL; +} + +_public_ sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot) { + assert_return(slot, NULL); + + return slot->bus; +} + +_public_ void *sd_bus_slot_get_userdata(sd_bus_slot *slot) { + assert_return(slot, NULL); + + return slot->userdata; +} + +_public_ void *sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata) { + void *ret; + + assert_return(slot, NULL); + + ret = slot->userdata; + slot->userdata = userdata; + + return ret; +} + +_public_ sd_bus_message *sd_bus_slot_get_current_message(sd_bus_slot *slot) { + assert_return(slot, NULL); + assert_return(slot->type >= 0, NULL); + + if (slot->bus->current_slot != slot) + return NULL; + + return slot->bus->current_message; +} + +_public_ sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *slot) { + assert_return(slot, NULL); + assert_return(slot->type >= 0, NULL); + + if (slot->bus->current_slot != slot) + return NULL; + + return slot->bus->current_handler; +} + +_public_ void* sd_bus_slot_get_current_userdata(sd_bus_slot *slot) { + assert_return(slot, NULL); + assert_return(slot->type >= 0, NULL); + + if (slot->bus->current_slot != slot) + return NULL; + + return slot->bus->current_userdata; +} + +_public_ int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description) { + assert_return(slot, -EINVAL); + + return free_and_strdup(&slot->description, description); +} + +_public_ int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description) { + assert_return(slot, -EINVAL); + assert_return(description, -EINVAL); + assert_return(slot->description, -ENXIO); + + *description = slot->description; + return 0; +} diff --git a/src/libsystemd/src/sd-bus/bus-slot.h b/src/libsystemd/src/sd-bus/bus-slot.h new file mode 100644 index 0000000000..b862799d1c --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-slot.h @@ -0,0 +1,28 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 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 . +***/ + +#include + +#include "bus-internal.h" + +sd_bus_slot *bus_slot_allocate(sd_bus *bus, bool floating, BusSlotType type, size_t extra, void *userdata); + +void bus_slot_disconnect(sd_bus_slot *slot); diff --git a/src/libsystemd/src/sd-bus/bus-socket.c b/src/libsystemd/src/sd-bus/bus-socket.c new file mode 100644 index 0000000000..1486d7cd55 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-socket.c @@ -0,0 +1,1064 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include + +#include +#include + +#include "alloc-util.h" +#include "bus-internal.h" +#include "bus-message.h" +#include "bus-socket.h" +#include "fd-util.h" +#include "formats-util.h" +#include "hexdecoct.h" +#include "macro.h" +#include "missing.h" +#include "selinux-util.h" +#include "signal-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "user-util.h" +#include "utf8.h" +#include "util.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_safe(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 == strlen("\r\nAGREE_UNIX_FD")) && + memcmp(e + 2, "AGREE_UNIX_FD", strlen("AGREE_UNIX_FD")) == 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; + size_t len; + int r; + + if (!b->anonymous_auth) + return 0; + + if (l <= 0) + return 1; + + assert(p[0] == ' '); + p++; l--; + + if (l % 2 != 0) + return 0; + + r = unhexmem(p, l, (void **) &token, &len); + if (r < 0) + return 0; + + if (memchr(token, 0, len)) + 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; + size_t len; + 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; + + r = unhexmem(p, l, (void**) &token, &len); + if (r < 0) + return 0; + + if (memchr(token, 0, len)) + 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_safe(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); + + xsprintf(t, "OK " SD_ID128_FORMAT_STR "\r\n", SD_ID128_FORMAT_VAL(b->server_id)); + + 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)]; + } control; + 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; + + 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) { + struct cmsghdr *cmsg; + + CMSG_FOREACH(cmsg, &mh) + 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 + log_debug("Got unexpected auxiliary data with level=%d and type=%d", + cmsg->cmsg_level, cmsg->cmsg_type); + } + + r = bus_socket_auth_verify(b); + if (r != 0) + return r; + + return 1; +} + +void bus_socket_setup(sd_bus *b) { + assert(b); + + /* 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) { + int r; + + assert(b); + + /* Get the peer for socketpair() sockets */ + b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0; + + /* Get the SELinux context of the peer */ + if (mac_selinux_have()) { + r = getpeersec(b->input_fd, &b->label); + if (r < 0 && r != -EOPNOTSUPP) + log_debug_errno(r, "Failed to determine peer security context: %m"); + } +} + +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[DECIMAL_STR_MAX(uid_t) + 1]; + + auth_prefix = "\0AUTH EXTERNAL "; + + xsprintf(text, UID_FMT, geteuid()); + + 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) { + safe_close_pair(s); + return -errno; + } + if (pid == 0) { + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + 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) + safe_close(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); + } + + safe_close(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_safe(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 = { + .msg_iov = iov, + .msg_iovlen = m->n_iovec, + }; + + if (m->n_fds > 0) { + struct cmsghdr *control; + + mh.msg_control = control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds)); + mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds); + control->cmsg_level = SOL_SOCKET; + control->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(control), m->fds, sizeof(int) * m->n_fds); + } + + 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, + 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)]; + } control; + 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; + + 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) { + struct cmsghdr *cmsg; + + CMSG_FOREACH(cmsg, &mh) + 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_safe(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int)); + bus->fds = f; + bus->n_fds += n; + } else + log_debug("Got unexpected auxiliary data with level=%d and type=%d", + cmsg->cmsg_level, cmsg->cmsg_type); + } + + 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/src/sd-bus/bus-socket.h b/src/libsystemd/src/sd-bus/bus-socket.h new file mode 100644 index 0000000000..6e1d32e6a7 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-socket.h @@ -0,0 +1,37 @@ +#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 . +***/ + +#include + +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/src/sd-bus/bus-track.c b/src/libsystemd/src/sd-bus/bus-track.c new file mode 100644 index 0000000000..81e6d22816 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-track.c @@ -0,0 +1,337 @@ +/*** + 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 . +***/ + +#include + +#include "alloc-util.h" +#include "bus-internal.h" +#include "bus-track.h" +#include "bus-util.h" + +struct sd_bus_track { + unsigned n_ref; + sd_bus *bus; + sd_bus_track_handler_t handler; + void *userdata; + Hashmap *names; + LIST_FIELDS(sd_bus_track, queue); + Iterator iterator; + bool in_queue; + bool modified; +}; + +#define MATCH_PREFIX \ + "type='signal'," \ + "sender='org.freedesktop.DBus'," \ + "path='/org/freedesktop/DBus'," \ + "interface='org.freedesktop.DBus'," \ + "member='NameOwnerChanged'," \ + "arg0='" + +#define MATCH_SUFFIX \ + "'" + +#define MATCH_FOR_NAME(name) \ + ({ \ + char *_x; \ + size_t _l = strlen(name); \ + _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \ + strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \ + _x; \ + }) + +static void bus_track_add_to_queue(sd_bus_track *track) { + assert(track); + + if (track->in_queue) + return; + + if (!track->handler) + return; + + LIST_PREPEND(queue, track->bus->track_queue, track); + track->in_queue = true; +} + +static void bus_track_remove_from_queue(sd_bus_track *track) { + assert(track); + + if (!track->in_queue) + return; + + LIST_REMOVE(queue, track->bus->track_queue, track); + track->in_queue = false; +} + +_public_ int sd_bus_track_new( + sd_bus *bus, + sd_bus_track **track, + sd_bus_track_handler_t handler, + void *userdata) { + + sd_bus_track *t; + + assert_return(bus, -EINVAL); + assert_return(track, -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + t = new0(sd_bus_track, 1); + if (!t) + return -ENOMEM; + + t->n_ref = 1; + t->handler = handler; + t->userdata = userdata; + t->bus = sd_bus_ref(bus); + + bus_track_add_to_queue(t); + + *track = t; + return 0; +} + +_public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) { + + if (!track) + return NULL; + + assert(track->n_ref > 0); + + track->n_ref++; + + return track; +} + +_public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) { + const char *n; + + if (!track) + return NULL; + + assert(track->n_ref > 0); + + if (track->n_ref > 1) { + track->n_ref--; + return NULL; + } + + while ((n = hashmap_first_key(track->names))) + sd_bus_track_remove_name(track, n); + + bus_track_remove_from_queue(track); + hashmap_free(track->names); + sd_bus_unref(track->bus); + free(track); + + return NULL; +} + +static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + sd_bus_track *track = userdata; + const char *name, *old, *new; + int r; + + assert(message); + assert(track); + + r = sd_bus_message_read(message, "sss", &name, &old, &new); + if (r < 0) + return 0; + + sd_bus_track_remove_name(track, name); + return 0; +} + +_public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) { + _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL; + _cleanup_free_ char *n = NULL; + const char *match; + int r; + + assert_return(track, -EINVAL); + assert_return(service_name_is_valid(name), -EINVAL); + + r = hashmap_ensure_allocated(&track->names, &string_hash_ops); + if (r < 0) + return r; + + n = strdup(name); + if (!n) + return -ENOMEM; + + /* First, subscribe to this name */ + match = MATCH_FOR_NAME(n); + r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track); + if (r < 0) + return r; + + r = hashmap_put(track->names, n, slot); + if (r == -EEXIST) + return 0; + if (r < 0) + return r; + + /* Second, check if it is currently existing, or maybe + * doesn't, or maybe disappeared already. */ + r = sd_bus_get_name_creds(track->bus, n, 0, NULL); + if (r < 0) { + hashmap_remove(track->names, n); + return r; + } + + n = NULL; + slot = NULL; + + bus_track_remove_from_queue(track); + track->modified = true; + + return 1; +} + +_public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) { + _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL; + _cleanup_free_ char *n = NULL; + + assert_return(name, -EINVAL); + + if (!track) + return 0; + + slot = hashmap_remove2(track->names, (char*) name, (void**) &n); + if (!slot) + return 0; + + if (hashmap_isempty(track->names)) + bus_track_add_to_queue(track); + + track->modified = true; + + return 1; +} + +_public_ unsigned sd_bus_track_count(sd_bus_track *track) { + if (!track) + return 0; + + return hashmap_size(track->names); +} + +_public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) { + assert_return(track, NULL); + assert_return(name, NULL); + + return hashmap_get(track->names, (void*) name) ? name : NULL; +} + +_public_ const char* sd_bus_track_first(sd_bus_track *track) { + const char *n = NULL; + + if (!track) + return NULL; + + track->modified = false; + track->iterator = ITERATOR_FIRST; + + hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n); + return n; +} + +_public_ const char* sd_bus_track_next(sd_bus_track *track) { + const char *n = NULL; + + if (!track) + return NULL; + + if (track->modified) + return NULL; + + hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n); + return n; +} + +_public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) { + const char *sender; + + assert_return(track, -EINVAL); + assert_return(m, -EINVAL); + + sender = sd_bus_message_get_sender(m); + if (!sender) + return -EINVAL; + + return sd_bus_track_add_name(track, sender); +} + +_public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) { + const char *sender; + + assert_return(track, -EINVAL); + assert_return(m, -EINVAL); + + sender = sd_bus_message_get_sender(m); + if (!sender) + return -EINVAL; + + return sd_bus_track_remove_name(track, sender); +} + +_public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) { + assert_return(track, NULL); + + return track->bus; +} + +void bus_track_dispatch(sd_bus_track *track) { + int r; + + assert(track); + assert(track->in_queue); + assert(track->handler); + + bus_track_remove_from_queue(track); + + sd_bus_track_ref(track); + + r = track->handler(track, track->userdata); + if (r < 0) + log_debug_errno(r, "Failed to process track handler: %m"); + else if (r == 0) + bus_track_add_to_queue(track); + + sd_bus_track_unref(track); +} + +_public_ void *sd_bus_track_get_userdata(sd_bus_track *track) { + assert_return(track, NULL); + + return track->userdata; +} + +_public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) { + void *ret; + + assert_return(track, NULL); + + ret = track->userdata; + track->userdata = userdata; + + return ret; +} diff --git a/src/libsystemd/src/sd-bus/bus-track.h b/src/libsystemd/src/sd-bus/bus-track.h new file mode 100644 index 0000000000..7d93a727d6 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-track.h @@ -0,0 +1,22 @@ +#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 . +***/ + +void bus_track_dispatch(sd_bus_track *track); diff --git a/src/libsystemd/src/sd-bus/bus-type.c b/src/libsystemd/src/sd-bus/bus-type.c new file mode 100644 index 0000000000..c692afc580 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-type.c @@ -0,0 +1,176 @@ +/*** + 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 . +***/ + +#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/src/sd-bus/bus-type.h b/src/libsystemd/src/sd-bus/bus-type.h new file mode 100644 index 0000000000..7169b0f765 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-type.h @@ -0,0 +1,37 @@ +#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 . +***/ + +#include + +#include + +#include "macro.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_; +/* "trivial" is systemd's term for what the D-Bus Specification calls + * a "fixed type": that is, a basic type of fixed length */ +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/src/sd-bus/kdbus.h b/src/libsystemd/src/sd-bus/kdbus.h new file mode 100644 index 0000000000..ecffc6b13c --- /dev/null +++ b/src/libsystemd/src/sd-bus/kdbus.h @@ -0,0 +1,980 @@ +/* + * 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. + */ + +#ifndef _UAPI_KDBUS_H_ +#define _UAPI_KDBUS_H_ + +#include +#include + +#define KDBUS_IOCTL_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) +#define KDBUS_FLAG_NEGOTIATE (1ULL << 63) + +/** + * 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; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_notify_name_change - name registry change message + * @old_id: ID and flags of former owner of a name + * @new_id: 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_id; + struct kdbus_notify_id_change new_id; + char name[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_creds - process credentials + * @uid: User ID + * @euid: Effective UID + * @suid: Saved UID + * @fsuid: Filesystem UID + * @gid: Group ID + * @egid: Effective GID + * @sgid: Saved GID + * @fsgid: Filesystem GID + * + * Attached to: + * KDBUS_ITEM_CREDS + */ +struct kdbus_creds { + __u64 uid; + __u64 euid; + __u64 suid; + __u64 fsuid; + __u64 gid; + __u64 egid; + __u64 sgid; + __u64 fsgid; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_pids - process identifiers + * @pid: Process ID + * @tid: Thread ID + * @ppid: Parent process ID + * + * The PID and TID of a process. + * + * Attached to: + * KDBUS_ITEM_PIDS + */ +struct kdbus_pids { + __u64 pid; + __u64 tid; + __u64 ppid; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_caps - process capabilities + * @last_cap: Highest currently known capability bit + * @caps: Variable number of 32-bit capabilities flags + * + * Contains a variable number of 32-bit capabilities flags. + * + * Attached to: + * KDBUS_ITEM_CAPS + */ +struct kdbus_caps { + __u32 last_cap; + __u32 caps[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_audit - audit information + * @sessionid: The audit session ID + * @loginuid: The audit login uid + * + * Attached to: + * KDBUS_ITEM_AUDIT + */ +struct kdbus_audit { + __u32 sessionid; + __u32 loginuid; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_timestamp + * @seqnum: Global per-domain 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; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_vec - I/O vector for kdbus payload items + * @size: The size of the vector + * @address: Memory address of data buffer + * @offset: Offset in the in-message payload memory, + * relative to the message head + * + * Attached to: + * KDBUS_ITEM_PAYLOAD_VEC, KDBUS_ITEM_PAYLOAD_OFF + */ +struct kdbus_vec { + __u64 size; + union { + __u64 address; + __u64 offset; + }; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_bloom_parameter - bus-wide bloom parameters + * @size: Size of the bit field in bytes (m / 8) + * @n_hash: Number of hash functions used (k) + */ +struct kdbus_bloom_parameter { + __u64 size; + __u64 n_hash; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_bloom_filter - bloom filter containing n elements + * @generation: Generation of the element set in the filter + * @data: Bit field, multiple of 8 bytes + */ +struct kdbus_bloom_filter { + __u64 generation; + __u64 data[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_memfd - a kdbus memfd + * @start: The offset into the memfd where the segment starts + * @size: The size of the memfd segment + * @fd: The file descriptor number + * @__pad: Padding to ensure proper alignment and size + * + * Attached to: + * KDBUS_ITEM_PAYLOAD_MEMFD + */ +struct kdbus_memfd { + __u64 start; + __u64 size; + int fd; + __u32 __pad; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_name - a registered well-known name with its flags + * @flags: Flags from KDBUS_NAME_* + * @name: Well-known name + * + * Attached to: + * KDBUS_ITEM_OWNED_NAME + */ +struct kdbus_name { + __u64 flags; + char name[0]; +} __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_OWN: Allow to own a well-known name + * Implies KDBUS_POLICY_TALK and KDBUS_POLICY_SEE + * @KDBUS_POLICY_TALK: Allow communication to a well-known name + * Implies KDBUS_POLICY_SEE + * @KDBUS_POLICY_SEE: Allow to see a well-known name + */ +enum kdbus_policy_type { + KDBUS_POLICY_SEE = 0, + KDBUS_POLICY_TALK, + KDBUS_POLICY_OWN, +}; + +/** + * struct kdbus_policy_access - policy access item + * @type: One of KDBUS_POLICY_ACCESS_* types + * @access: Access to grant + * @id: For KDBUS_POLICY_ACCESS_USER, the uid + * For KDBUS_POLICY_ACCESS_GROUP, the gid + */ +struct kdbus_policy_access { + __u64 type; /* USER, GROUP, WORLD */ + __u64 access; /* OWN, TALK, SEE */ + __u64 id; /* uid, gid, 0 */ +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_attach_flags - flags for metadata attachments + * @KDBUS_ATTACH_TIMESTAMP: Timestamp + * @KDBUS_ATTACH_CREDS: Credentials + * @KDBUS_ATTACH_PIDS: PIDs + * @KDBUS_ATTACH_AUXGROUPS: Auxiliary groups + * @KDBUS_ATTACH_NAMES: Well-known names + * @KDBUS_ATTACH_TID_COMM: The "comm" process identifier of the TID + * @KDBUS_ATTACH_PID_COMM: The "comm" process identifier of the PID + * @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_DESCRIPTION: The human-readable connection name + * @_KDBUS_ATTACH_ALL: All of the above + * @_KDBUS_ATTACH_ANY: Wildcard match to enable any kind of + * metatdata. + */ +enum kdbus_attach_flags { + KDBUS_ATTACH_TIMESTAMP = 1ULL << 0, + KDBUS_ATTACH_CREDS = 1ULL << 1, + KDBUS_ATTACH_PIDS = 1ULL << 2, + KDBUS_ATTACH_AUXGROUPS = 1ULL << 3, + KDBUS_ATTACH_NAMES = 1ULL << 4, + KDBUS_ATTACH_TID_COMM = 1ULL << 5, + KDBUS_ATTACH_PID_COMM = 1ULL << 6, + KDBUS_ATTACH_EXE = 1ULL << 7, + KDBUS_ATTACH_CMDLINE = 1ULL << 8, + KDBUS_ATTACH_CGROUP = 1ULL << 9, + KDBUS_ATTACH_CAPS = 1ULL << 10, + KDBUS_ATTACH_SECLABEL = 1ULL << 11, + KDBUS_ATTACH_AUDIT = 1ULL << 12, + KDBUS_ATTACH_CONN_DESCRIPTION = 1ULL << 13, + _KDBUS_ATTACH_ALL = (1ULL << 14) - 1, + _KDBUS_ATTACH_ANY = ~0ULL +}; + +/** + * 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_NEGOTIATE: Negotiate supported 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_CANCEL_FD: FD used to cancel a synchronous + * operation by writing to it from + * userspace + * @KDBUS_ITEM_BLOOM_PARAMETER: Bus-wide bloom parameters, used with + * KDBUS_CMD_BUS_MAKE, carries a + * struct kdbus_bloom_parameter + * @KDBUS_ITEM_BLOOM_FILTER: Bloom filter carried with a message, + * used to match against a bloom mask of a + * connection, carries a struct + * kdbus_bloom_filter + * @KDBUS_ITEM_BLOOM_MASK: Bloom mask used to match against a + * message'sbloom filter + * @KDBUS_ITEM_DST_NAME: Destination's well-known name + * @KDBUS_ITEM_MAKE_NAME: Name of domain, bus, endpoint + * @KDBUS_ITEM_ATTACH_FLAGS_SEND: Attach-flags, used for updating which + * metadata a connection opts in to send + * @KDBUS_ITEM_ATTACH_FLAGS_RECV: Attach-flags, used for updating which + * metadata a connection requests to + * receive for each reeceived message + * @KDBUS_ITEM_ID: Connection ID + * @KDBUS_ITEM_NAME: Well-know name with flags + * @_KDBUS_ITEM_ATTACH_BASE: Start of metadata attach items + * @KDBUS_ITEM_TIMESTAMP: Timestamp + * @KDBUS_ITEM_CREDS: Process credentials + * @KDBUS_ITEM_PIDS: Process identifiers + * @KDBUS_ITEM_AUXGROUPS: Auxiliary process groups + * @KDBUS_ITEM_OWNED_NAME: A name owned by the associated + * connection + * @KDBUS_ITEM_TID_COMM: Thread ID "comm" identifier + * (Don't trust this, see below.) + * @KDBUS_ITEM_PID_COMM: Process ID "comm" identifier + * (Don't trust this, see below.) + * @KDBUS_ITEM_EXE: The path of the executable + * (Don't trust this, see below.) + * @KDBUS_ITEM_CMDLINE: The process command line + * (Don't trust this, see below.) + * @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_DESCRIPTION: The connection's human-readable name + * (debugging) + * @_KDBUS_ITEM_POLICY_BASE: Start of policy items + * @KDBUS_ITEM_POLICY_ACCESS: Policy access block + * @_KDBUS_ITEM_KERNEL_BASE: Start of kernel-generated message items + * @KDBUS_ITEM_NAME_ADD: Notification in kdbus_notify_name_change + * @KDBUS_ITEM_NAME_REMOVE: Notification in kdbus_notify_name_change + * @KDBUS_ITEM_NAME_CHANGE: Notification in kdbus_notify_name_change + * @KDBUS_ITEM_ID_ADD: Notification in kdbus_notify_id_change + * @KDBUS_ITEM_ID_REMOVE: Notification in kdbus_notify_id_change + * @KDBUS_ITEM_REPLY_TIMEOUT: Timeout has been reached + * @KDBUS_ITEM_REPLY_DEAD: Destination died + * + * N.B: The process and thread COMM fields, as well as the CMDLINE and + * EXE fields may be altered by unprivileged processes und should + * hence *not* used for security decisions. Peers should make use of + * these items only for informational purposes, such as generating log + * records. + */ +enum kdbus_item_type { + _KDBUS_ITEM_NULL, + _KDBUS_ITEM_USER_BASE, + KDBUS_ITEM_NEGOTIATE = _KDBUS_ITEM_USER_BASE, + KDBUS_ITEM_PAYLOAD_VEC, + KDBUS_ITEM_PAYLOAD_OFF, + KDBUS_ITEM_PAYLOAD_MEMFD, + KDBUS_ITEM_FDS, + KDBUS_ITEM_CANCEL_FD, + KDBUS_ITEM_BLOOM_PARAMETER, + KDBUS_ITEM_BLOOM_FILTER, + KDBUS_ITEM_BLOOM_MASK, + KDBUS_ITEM_DST_NAME, + KDBUS_ITEM_MAKE_NAME, + KDBUS_ITEM_ATTACH_FLAGS_SEND, + KDBUS_ITEM_ATTACH_FLAGS_RECV, + KDBUS_ITEM_ID, + KDBUS_ITEM_NAME, + KDBUS_ITEM_DST_ID, + + /* keep these item types in sync with KDBUS_ATTACH_* flags */ + _KDBUS_ITEM_ATTACH_BASE = 0x1000, + KDBUS_ITEM_TIMESTAMP = _KDBUS_ITEM_ATTACH_BASE, + KDBUS_ITEM_CREDS, + KDBUS_ITEM_PIDS, + KDBUS_ITEM_AUXGROUPS, + KDBUS_ITEM_OWNED_NAME, + KDBUS_ITEM_TID_COMM, + KDBUS_ITEM_PID_COMM, + KDBUS_ITEM_EXE, + KDBUS_ITEM_CMDLINE, + KDBUS_ITEM_CGROUP, + KDBUS_ITEM_CAPS, + KDBUS_ITEM_SECLABEL, + KDBUS_ITEM_AUDIT, + KDBUS_ITEM_CONN_DESCRIPTION, + + _KDBUS_ITEM_POLICY_BASE = 0x2000, + KDBUS_ITEM_POLICY_ACCESS = _KDBUS_ITEM_POLICY_BASE, + + _KDBUS_ITEM_KERNEL_BASE = 0x8000, + 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 + * @bloom_parameter: KDBUS_ITEM_BLOOM_PARAMETER + * @bloom_filter: KDBUS_ITEM_BLOOM_FILTER + * @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_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_pids pids; + struct kdbus_audit audit; + struct kdbus_caps caps; + struct kdbus_timestamp timestamp; + struct kdbus_name name; + struct kdbus_bloom_parameter bloom_parameter; + struct kdbus_bloom_filter bloom_filter; + struct kdbus_memfd memfd; + int fds[0]; + struct kdbus_notify_name_change name_change; + struct kdbus_notify_id_change id_change; + struct kdbus_policy_access policy_access; + }; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_msg_flags - type of message + * @KDBUS_MSG_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_NO_AUTO_START: Do not start a service if the addressed + * name is not currently active. This flag is + * not looked at by the kernel but only + * serves as hint for userspace implementations. + * @KDBUS_MSG_SIGNAL: Treat this message as signal + */ +enum kdbus_msg_flags { + KDBUS_MSG_EXPECT_REPLY = 1ULL << 0, + KDBUS_MSG_NO_AUTO_START = 1ULL << 1, + KDBUS_MSG_SIGNAL = 1ULL << 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" + * + * Any payload-type is accepted. Common types will get added here once + * established. + */ +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_*), userspace → kernel + * @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, and the send command is + * executed asynchronously, a kernel-generated message + * with an attached KDBUS_ITEM_REPLY_TIMEOUT item + * is sent to @src_id. For synchronously executed send + * command, the value denotes the maximum time the call + * blocks to wait for a reply. The timeout is expected in + * nanoseconds and as absolute CLOCK_MONOTONIC value. + * @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 + * @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; + }; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_msg_info - returned message container + * @offset: Offset of kdbus_msg slice in pool + * @msg_size: Copy of the kdbus_msg.size field + * @return_flags: Command return flags, kernel → userspace + */ +struct kdbus_msg_info { + __u64 offset; + __u64 msg_size; + __u64 return_flags; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_send_flags - flags for sending messages + * @KDBUS_SEND_SYNC_REPLY: Wait for destination connection to + * reply to this message. The + * KDBUS_CMD_SEND ioctl() will block + * until the reply is received, and + * reply in struct kdbus_cmd_send will + * yield the offset in the sender's pool + * where the reply can be found. + * This flag is only valid if + * @KDBUS_MSG_EXPECT_REPLY is set as well. + */ +enum kdbus_send_flags { + KDBUS_SEND_SYNC_REPLY = 1ULL << 0, +}; + +/** + * struct kdbus_cmd_send - send message + * @size: Overall size of this structure + * @flags: Flags to change send behavior (KDBUS_SEND_*) + * @return_flags: Command return flags, kernel → userspace + * @msg_address: Storage address of the kdbus_msg to send + * @reply: Storage for message reply if KDBUS_SEND_SYNC_REPLY + * was given + * @items: Additional items for this command + */ +struct kdbus_cmd_send { + __u64 size; + __u64 flags; + __u64 return_flags; + __u64 msg_address; + struct kdbus_msg_info 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 = 1ULL << 0, + KDBUS_RECV_DROP = 1ULL << 1, + KDBUS_RECV_USE_PRIORITY = 1ULL << 2, +}; + +/** + * enum kdbus_recv_return_flags - return flags for message receive commands + * @KDBUS_RECV_RETURN_INCOMPLETE_FDS: One or more file descriptors could not + * be installed. These descriptors in + * KDBUS_ITEM_FDS will carry the value -1. + * @KDBUS_RECV_RETURN_DROPPED_MSGS: There have been dropped messages since + * the last time a message was received. + * The 'dropped_msgs' counter contains the + * number of messages dropped pool + * overflows or other missed broadcasts. + */ +enum kdbus_recv_return_flags { + KDBUS_RECV_RETURN_INCOMPLETE_FDS = 1ULL << 0, + KDBUS_RECV_RETURN_DROPPED_MSGS = 1ULL << 1, +}; + +/** + * struct kdbus_cmd_recv - struct to de-queue a buffered message + * @size: Overall size of this object + * @flags: KDBUS_RECV_* flags, userspace → kernel + * @return_flags: Command return flags, kernel → userspace + * @priority: Minimum priority of the messages to de-queue. Lowest + * values have the highest priority. + * @dropped_msgs: In case there were any dropped messages since the last + * time a message was received, this will be set to the + * number of lost messages and + * KDBUS_RECV_RETURN_DROPPED_MSGS will be set in + * 'return_flags'. This can only happen if the ioctl + * returns 0 or EAGAIN. + * @msg: Return storage for received message. + * @items: Additional items for this command. + * + * This struct is used with the KDBUS_CMD_RECV ioctl. + */ +struct kdbus_cmd_recv { + __u64 size; + __u64 flags; + __u64 return_flags; + __s64 priority; + __u64 dropped_msgs; + struct kdbus_msg_info msg; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_cmd_free - struct to free a slice of memory in the pool + * @size: Overall size of this structure + * @flags: Flags for the free command, userspace → kernel + * @return_flags: Command return flags, kernel → userspace + * @offset: The offset of the memory slice, as returned by other + * ioctls + * @items: Additional items to modify the behavior + * + * This struct is used with the KDBUS_CMD_FREE ioctl. + */ +struct kdbus_cmd_free { + __u64 size; + __u64 flags; + __u64 return_flags; + __u64 offset; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_hello_flags - flags for struct kdbus_cmd_hello + * @KDBUS_HELLO_ACCEPT_FD: The connection allows the reception 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_POLICY_HOLDER: Special-purpose connection which registers + * policy entries for a name. The provided name + * is not activated and not registered with the + * name database, it only allows unprivileged + * connections to acquire a name, talk or discover + * a service + * @KDBUS_HELLO_MONITOR: Special-purpose connection to monitor + * bus traffic + */ +enum kdbus_hello_flags { + KDBUS_HELLO_ACCEPT_FD = 1ULL << 0, + KDBUS_HELLO_ACTIVATOR = 1ULL << 1, + KDBUS_HELLO_POLICY_HOLDER = 1ULL << 2, + KDBUS_HELLO_MONITOR = 1ULL << 3, +}; + +/** + * struct kdbus_cmd_hello - struct to say hello to kdbus + * @size: The total size of the structure + * @flags: Connection flags (KDBUS_HELLO_*), userspace → kernel + * @return_flags: Command return flags, kernel → userspace + * @attach_flags_send: Mask of metadata to attach to each message sent + * off by this connection (KDBUS_ATTACH_*) + * @attach_flags_recv: Mask of metadata to attach to each message receieved + * by the new connection (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) + * @pool_size: Size of the connection's buffer where the received + * messages are placed + * @offset: Pool offset where items are returned to report + * additional information about the bus and the newly + * created connection. + * @items_size: Size of buffer returned in the pool slice at @offset. + * @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 flags; + __u64 return_flags; + __u64 attach_flags_send; + __u64 attach_flags_recv; + __u64 bus_flags; + __u64 id; + __u64 pool_size; + __u64 offset; + __u64 items_size; + __u8 id128[16]; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_info - connection information + * @size: total size of the struct + * @id: 64bit object ID + * @flags: object creation flags + * @items: list of items + * + * Note that the user is responsible for freeing the allocated memory with + * the KDBUS_CMD_FREE ioctl. + */ +struct kdbus_info { + __u64 size; + __u64 id; + __u64 flags; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_list_flags - what to include into the returned list + * @KDBUS_LIST_UNIQUE: active connections + * @KDBUS_LIST_ACTIVATORS: activator connections + * @KDBUS_LIST_NAMES: known well-known names + * @KDBUS_LIST_QUEUED: queued-up names + */ +enum kdbus_list_flags { + KDBUS_LIST_UNIQUE = 1ULL << 0, + KDBUS_LIST_NAMES = 1ULL << 1, + KDBUS_LIST_ACTIVATORS = 1ULL << 2, + KDBUS_LIST_QUEUED = 1ULL << 3, +}; + +/** + * struct kdbus_cmd_list - list connections + * @size: overall size of this object + * @flags: flags for the query (KDBUS_LIST_*), userspace → kernel + * @return_flags: command return flags, kernel → userspace + * @offset: Offset in the caller's pool buffer where an array of + * kdbus_info objects is stored. + * The user must use KDBUS_CMD_FREE to free the + * allocated memory. + * @list_size: size of returned list in bytes + * @items: Items for the command. Reserved for future use. + * + * This structure is used with the KDBUS_CMD_LIST ioctl. + */ +struct kdbus_cmd_list { + __u64 size; + __u64 flags; + __u64 return_flags; + __u64 offset; + __u64 list_size; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_cmd_info - struct used for KDBUS_CMD_CONN_INFO ioctl + * @size: The total size of the struct + * @flags: Flags for this ioctl, userspace → kernel + * @return_flags: Command return flags, kernel → userspace + * @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. + * @attach_flags: Set of attach flags to specify the set of information + * to receive, userspace → kernel + * @offset: Returned offset in the caller's pool buffer where the + * kdbus_info struct result is stored. The user must + * use KDBUS_CMD_FREE to free the allocated memory. + * @info_size: Output buffer to report size of data at @offset. + * @items: The optional item list, containing the + * well-known name to look up as a KDBUS_ITEM_NAME. + * 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_info. + */ +struct kdbus_cmd_info { + __u64 size; + __u64 flags; + __u64 return_flags; + __u64 id; + __u64 attach_flags; + __u64 offset; + __u64 info_size; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_cmd_match_flags - flags to control the KDBUS_CMD_MATCH_ADD ioctl + * @KDBUS_MATCH_REPLACE: If entries with the supplied cookie already + * exists, remove them before installing the new + * matches. + */ +enum kdbus_cmd_match_flags { + KDBUS_MATCH_REPLACE = 1ULL << 0, +}; + +/** + * struct kdbus_cmd_match - struct to add or remove matches + * @size: The total size of the struct + * @flags: Flags for match command (KDBUS_MATCH_*), + * userspace → kernel + * @return_flags: Command return flags, kernel → userspace + * @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_MATCH_ADD and + * KDBUS_CMD_MATCH_REMOVE ioctl. + */ +struct kdbus_cmd_match { + __u64 size; + __u64 flags; + __u64 return_flags; + __u64 cookie; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_make_flags - Flags for KDBUS_CMD_{BUS,ENDPOINT}_MAKE + * @KDBUS_MAKE_ACCESS_GROUP: Make the bus or endpoint node group-accessible + * @KDBUS_MAKE_ACCESS_WORLD: Make the bus or endpoint node world-accessible + */ +enum kdbus_make_flags { + KDBUS_MAKE_ACCESS_GROUP = 1ULL << 0, + KDBUS_MAKE_ACCESS_WORLD = 1ULL << 1, +}; + +/** + * enum kdbus_name_flags - flags for KDBUS_CMD_NAME_ACQUIRE + * @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 = 1ULL << 0, + KDBUS_NAME_ALLOW_REPLACEMENT = 1ULL << 1, + KDBUS_NAME_QUEUE = 1ULL << 2, + KDBUS_NAME_IN_QUEUE = 1ULL << 3, + KDBUS_NAME_ACTIVATOR = 1ULL << 4, +}; + +/** + * struct kdbus_cmd - generic ioctl payload + * @size: Overall size of this structure + * @flags: Flags for this ioctl, userspace → kernel + * @return_flags: Ioctl return flags, kernel → userspace + * @items: Additional items to modify the behavior + * + * This is a generic ioctl payload object. It's used by all ioctls that only + * take flags and items as input. + */ +struct kdbus_cmd { + __u64 size; + __u64 flags; + __u64 return_flags; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * Ioctl API + * + * KDBUS_CMD_BUS_MAKE: After opening the "control" node, this command + * creates a new bus with the specified + * name. The bus is immediately shut down and + * cleaned up when the opened file descriptor is + * closed. + * + * KDBUS_CMD_ENDPOINT_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_ENDPOINT_UPDATE: Update the properties of a custom enpoint. Used + * to update the policy. + * + * KDBUS_CMD_HELLO: By opening the bus node, a connection is + * created. After a HELLO the opened connection + * becomes an active peer on the bus. + * KDBUS_CMD_UPDATE: Update the properties of a connection. Used to + * update the metadata subscription mask and + * policy. + * KDBUS_CMD_BYEBYE: Disconnect a connection. If there are no + * messages queued up in the connection's pool, + * the call succeeds, and the handle is rendered + * unusable. Otherwise, -EBUSY is returned without + * any further side-effects. + * KDBUS_CMD_FREE: Release the allocated memory in the receiver's + * pool. + * 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_BUS_CREATOR_INFO: Retrieve information of the creator of the bus + * a connection is attached to. + * + * KDBUS_CMD_SEND: Send a message and pass data from userspace to + * the kernel. + * KDBUS_CMD_RECV: Receive a message from the kernel which is + * placed 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_LIST: Retrieve the list of all currently registered + * well-known and unique names. + * + * 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. + */ +enum kdbus_ioctl_type { + /* bus owner (00-0f) */ + KDBUS_CMD_BUS_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x00, + struct kdbus_cmd), + + /* endpoint owner (10-1f) */ + KDBUS_CMD_ENDPOINT_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x10, + struct kdbus_cmd), + KDBUS_CMD_ENDPOINT_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x11, + struct kdbus_cmd), + + /* connection owner (80-ff) */ + KDBUS_CMD_HELLO = _IOWR(KDBUS_IOCTL_MAGIC, 0x80, + struct kdbus_cmd_hello), + KDBUS_CMD_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x81, + struct kdbus_cmd), + KDBUS_CMD_BYEBYE = _IOW(KDBUS_IOCTL_MAGIC, 0x82, + struct kdbus_cmd), + KDBUS_CMD_FREE = _IOW(KDBUS_IOCTL_MAGIC, 0x83, + struct kdbus_cmd_free), + KDBUS_CMD_CONN_INFO = _IOR(KDBUS_IOCTL_MAGIC, 0x84, + struct kdbus_cmd_info), + KDBUS_CMD_BUS_CREATOR_INFO = _IOR(KDBUS_IOCTL_MAGIC, 0x85, + struct kdbus_cmd_info), + KDBUS_CMD_LIST = _IOR(KDBUS_IOCTL_MAGIC, 0x86, + struct kdbus_cmd_list), + + KDBUS_CMD_SEND = _IOW(KDBUS_IOCTL_MAGIC, 0x90, + struct kdbus_cmd_send), + KDBUS_CMD_RECV = _IOR(KDBUS_IOCTL_MAGIC, 0x91, + struct kdbus_cmd_recv), + + KDBUS_CMD_NAME_ACQUIRE = _IOW(KDBUS_IOCTL_MAGIC, 0xa0, + struct kdbus_cmd), + KDBUS_CMD_NAME_RELEASE = _IOW(KDBUS_IOCTL_MAGIC, 0xa1, + struct kdbus_cmd), + + KDBUS_CMD_MATCH_ADD = _IOW(KDBUS_IOCTL_MAGIC, 0xb0, + struct kdbus_cmd_match), + KDBUS_CMD_MATCH_REMOVE = _IOW(KDBUS_IOCTL_MAGIC, 0xb1, + struct kdbus_cmd_match), +}; + +#endif /* _UAPI_KDBUS_H_ */ diff --git a/src/libsystemd/src/sd-bus/sd-bus.c b/src/libsystemd/src/sd-bus/sd-bus.c new file mode 100644 index 0000000000..d3c194e135 --- /dev/null +++ b/src/libsystemd/src/sd-bus/sd-bus.c @@ -0,0 +1,3791 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "bus-container.h" +#include "bus-control.h" +#include "bus-internal.h" +#include "bus-kernel.h" +#include "bus-label.h" +#include "bus-message.h" +#include "bus-objects.h" +#include "bus-protocol.h" +#include "bus-slot.h" +#include "bus-socket.h" +#include "bus-track.h" +#include "bus-type.h" +#include "bus-util.h" +#include "cgroup-util.h" +#include "def.h" +#include "fd-util.h" +#include "hexdecoct.h" +#include "hostname-util.h" +#include "macro.h" +#include "missing.h" +#include "parse-util.h" +#include "string-util.h" +#include "strv.h" +#include "util.h" + +#define log_debug_bus_message(m) \ + do { \ + sd_bus_message *_mm = (m); \ + log_debug("Got message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " error=%s", \ + bus_message_type_to_string(_mm->header->type), \ + strna(sd_bus_message_get_sender(_mm)), \ + strna(sd_bus_message_get_destination(_mm)), \ + strna(sd_bus_message_get_path(_mm)), \ + strna(sd_bus_message_get_interface(_mm)), \ + strna(sd_bus_message_get_member(_mm)), \ + BUS_MESSAGE_COOKIE(_mm), \ + _mm->reply_cookie, \ + strna(_mm->error.message)); \ + } while (false) + +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 thread_local sd_bus *default_system_bus = NULL; +static thread_local sd_bus *default_user_bus = NULL; +static thread_local sd_bus *default_starter_bus = NULL; + +static void bus_close_fds(sd_bus *b) { + assert(b); + + detach_io_events(b); + + if (b->input_fd != b->output_fd) + safe_close(b->output_fd); + b->output_fd = b->input_fd = safe_close(b->input_fd); +} + +static void bus_reset_queues(sd_bus *b) { + assert(b); + + while (b->rqueue_size > 0) + sd_bus_message_unref(b->rqueue[--b->rqueue_size]); + + b->rqueue = mfree(b->rqueue); + b->rqueue_allocated = 0; + + while (b->wqueue_size > 0) + sd_bus_message_unref(b->wqueue[--b->wqueue_size]); + + b->wqueue = mfree(b->wqueue); + b->wqueue_allocated = 0; +} + +static void bus_free(sd_bus *b) { + sd_bus_slot *s; + + assert(b); + assert(!b->track_queue); + + b->state = BUS_CLOSED; + + sd_bus_detach_event(b); + + while ((s = b->slots)) { + /* At this point only floating slots can still be + * around, because the non-floating ones keep a + * reference to the bus, and we thus couldn't be + * destructing right now... We forcibly disconnect the + * slots here, so that they still can be referenced by + * apps, but are dead. */ + + assert(s->floating); + bus_slot_disconnect(s); + sd_bus_slot_unref(s); + } + + if (b->default_bus_ptr) + *b->default_bus_ptr = NULL; + + bus_close_fds(b); + + if (b->kdbus_buffer) + munmap(b->kdbus_buffer, KDBUS_POOL_SIZE); + + free(b->label); + 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->description); + + free(b->exec_path); + strv_free(b->exec_argv); + + close_many(b->fds, b->n_fds); + free(b->fds); + + bus_reset_queues(b); + + ordered_hashmap_free_free(b->reply_callbacks); + prioq_free(b->reply_callbacks_prioq); + + assert(b->match_callbacks.type == BUS_MATCH_ROOT); + bus_match_free(&b->match_callbacks); + + hashmap_free_free(b->vtable_methods); + hashmap_free_free(b->vtable_properties); + + assert(hashmap_isempty(b->nodes)); + 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, -EBADF); + assert_return(output_fd >= 0, -EBADF); + 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_set_monitor(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_MONITOR, 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_timestamp(sd_bus *bus, int b) { + uint64_t new_flags; + assert_return(bus, -EINVAL); + assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + new_flags = bus->attach_flags; + SET_FLAG(new_flags, KDBUS_ATTACH_TIMESTAMP, b); + + if (bus->attach_flags == new_flags) + return 0; + + bus->attach_flags = new_flags; + if (bus->state != BUS_UNSET && bus->is_kernel) + bus_kernel_realize_attach_flags(bus); + + return 0; +} + +_public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) { + uint64_t new_flags; + + assert_return(bus, -EINVAL); + assert_return(mask <= _SD_BUS_CREDS_ALL, -EINVAL); + assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + SET_FLAG(bus->creds_mask, mask, b); + + /* The well knowns we need unconditionally, so that matches can work */ + bus->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME; + + /* Make sure we don't lose the timestamp flag */ + new_flags = (bus->attach_flags & KDBUS_ATTACH_TIMESTAMP) | attach_flags_to_kdbus(bus->creds_mask); + if (bus->attach_flags == new_flags) + return 0; + + bus->attach_flags = new_flags; + if (bus->state != BUS_UNSET && bus->is_kernel) + bus_kernel_realize_attach_flags(bus); + + return 0; +} + +_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; +} + +_public_ int sd_bus_set_description(sd_bus *bus, const char *description) { + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return free_and_strdup(&bus->description, description); +} + +_public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->allow_interactive_authorization = !!b; + return 0; +} + +_public_ int sd_bus_get_allow_interactive_authorization(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->allow_interactive_authorization; +} + +static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { + const char *s; + sd_bus *bus; + int r; + + assert(reply); + bus = reply->bus; + assert(bus); + assert(bus->state == BUS_HELLO || bus->state == BUS_CLOSING); + + r = sd_bus_message_get_errno(reply); + 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_(sd_bus_message_unrefp) 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, + &m, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "Hello"); + if (r < 0) + return r; + + return sd_bus_call_async(bus, NULL, m, hello_callback, NULL, 0); +} + +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, *pid = 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; + + r = parse_address_key(p, "pid", &pid); + if (r < 0) + return r; + else if (r > 0) + continue; + + skip_address_key(p); + } + + if (!machine == !pid) + return -EINVAL; + + if (machine) { + if (!machine_name_is_valid(machine)) + return -EINVAL; + + free(b->machine); + b->machine = machine; + machine = NULL; + } else { + b->machine = mfree(b->machine); + } + + if (pid) { + r = parse_pid(pid, &b->nspid); + if (r < 0) + return r; + } else + b->nspid = 0; + + 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 = SOCKADDR_UN_LEN(b->sockaddr.un); + + return 0; +} + +static int parse_container_kernel_address(sd_bus *b, const char **p, char **guid) { + _cleanup_free_ char *machine = NULL, *pid = 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; + + r = parse_address_key(p, "pid", &pid); + if (r < 0) + return r; + else if (r > 0) + continue; + + skip_address_key(p); + } + + if (!machine == !pid) + return -EINVAL; + + if (machine) { + if (!machine_name_is_valid(machine)) + return -EINVAL; + + free(b->machine); + b->machine = machine; + machine = NULL; + } else { + b->machine = mfree(b->machine); + } + + if (pid) { + r = parse_pid(pid, &b->nspid); + if (r < 0) + return r; + } else + b->nspid = 0; + + r = free_and_strdup(&b->kernel, "/sys/fs/kdbus/0-system/bus"); + if (r < 0) + return r; + + return 0; +} + +static void bus_reset_parsed_address(sd_bus *b) { + assert(b); + + zero(b->sockaddr); + b->sockaddr_size = 0; + b->exec_argv = strv_free(b->exec_argv); + b->exec_path = mfree(b->exec_path); + b->server_id = SD_ID128_NULL; + b->kernel = mfree(b->kernel); + b->machine = mfree(b->machine); + b->nspid = 0; +} + +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-machine-unix:")) { + + a += 15; + r = parse_container_unix_address(b, &a, &guid); + if (r < 0) + return r; + + break; + } else if (startswith(a, "x-machine-kernel:")) { + + a += 17; + 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) { + bool container_kdbus_available = false; + bool kdbus_available = false; + int r; + + assert(b); + + for (;;) { + bool skipped = false; + + bus_close_fds(b); + + /* + * Usually, if you provide multiple different bus-addresses, we + * try all of them in order. We use the first one that + * succeeds. However, if you mix kernel and unix addresses, we + * never try unix-addresses if a previous kernel address was + * tried and kdbus was available. This is required to prevent + * clients to fallback to the bus-proxy if kdbus is available + * but failed (eg., too many connections). + */ + + if (b->exec_path) + r = bus_socket_exec(b); + else if ((b->nspid > 0 || b->machine) && b->kernel) { + r = bus_container_connect_kernel(b); + if (r < 0 && !IN_SET(r, -ENOENT, -ESOCKTNOSUPPORT)) + container_kdbus_available = true; + + } else if ((b->nspid > 0 || b->machine) && b->sockaddr.sa.sa_family != AF_UNSPEC) { + if (!container_kdbus_available) + r = bus_container_connect_socket(b); + else + skipped = true; + + } else if (b->kernel) { + r = bus_kernel_connect(b); + if (r < 0 && !IN_SET(r, -ENOENT, -ESOCKTNOSUPPORT)) + kdbus_available = true; + + } else if (b->sockaddr.sa.sa_family != AF_UNSPEC) { + if (!kdbus_available) + r = bus_socket_connect(b); + else + skipped = true; + } 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) { + sd_bus_close(bus); + return r; + } + + return bus_send_hello(bus); +} + +_public_ int sd_bus_open(sd_bus **ret) { + const char *e; + sd_bus *b; + int r; + + assert_return(ret, -EINVAL); + + /* Let's connect to the starter bus if it is set, and + * otherwise to the bus that is appropropriate for the scope + * we are running in */ + + e = secure_getenv("DBUS_STARTER_BUS_TYPE"); + if (e) { + if (streq(e, "system")) + return sd_bus_open_system(ret); + else if (STR_IN_SET(e, "session", "user")) + return sd_bus_open_user(ret); + } + + e = secure_getenv("DBUS_STARTER_ADDRESS"); + if (!e) { + if (cg_pid_get_owner_uid(0, NULL) >= 0) + return sd_bus_open_user(ret); + else + return sd_bus_open_system(ret); + } + + r = sd_bus_new(&b); + if (r < 0) + return r; + + r = sd_bus_set_address(b, e); + if (r < 0) + goto fail; + + b->bus_client = true; + + /* We don't know whether the bus is trusted or not, so better + * be safe, and authenticate everything */ + b->trusted = false; + b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; + b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; + + r = sd_bus_start(b); + if (r < 0) + goto fail; + + *ret = b; + return 0; + +fail: + bus_free(b); + return r; +} + +int bus_set_address_system(sd_bus *b) { + const char *e; + assert(b); + + e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS"); + if (e) + return sd_bus_set_address(b, e); + + return sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_ADDRESS); +} + +_public_ int sd_bus_open_system(sd_bus **ret) { + sd_bus *b; + int r; + + assert_return(ret, -EINVAL); + + r = sd_bus_new(&b); + if (r < 0) + return r; + + r = bus_set_address_system(b); + if (r < 0) + goto fail; + + b->bus_client = true; + b->is_system = 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; + b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; + + r = sd_bus_start(b); + if (r < 0) + goto fail; + + *ret = b; + return 0; + +fail: + bus_free(b); + return r; +} + +int bus_set_address_user(sd_bus *b) { + const char *e; + uid_t uid; + int r; + + assert(b); + + e = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); + if (e) + return sd_bus_set_address(b, e); + + r = cg_pid_get_owner_uid(0, &uid); + if (r < 0) + uid = getuid(); + + e = secure_getenv("XDG_RUNTIME_DIR"); + if (e) { + _cleanup_free_ char *ee = NULL; + + ee = bus_address_escape(e); + if (!ee) + return -ENOMEM; + + (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, ee); + } else + (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT, uid); + + if (!b->address) + return -ENOMEM; + + return 0; +} + +_public_ int sd_bus_open_user(sd_bus **ret) { + sd_bus *b; + int r; + + assert_return(ret, -EINVAL); + + r = sd_bus_new(&b); + if (r < 0) + return r; + + r = bus_set_address_user(b); + if (r < 0) + return r; + + b->bus_client = true; + b->is_user = 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; +} + +int bus_set_address_system_remote(sd_bus *b, const char *host) { + _cleanup_free_ char *e = NULL; + char *m = NULL, *c = NULL; + + assert(b); + assert(host); + + /* Let's see if we shall enter some container */ + m = strchr(host, ':'); + if (m) { + m++; + + /* Let's make sure this is not a port of some kind, + * and is a valid machine name. */ + if (!in_charset(m, "0123456789") && machine_name_is_valid(m)) { + char *t; + + /* Cut out the host part */ + t = strndupa(host, m - host - 1); + e = bus_address_escape(t); + if (!e) + return -ENOMEM; + + c = strjoina(",argv4=--machine=", m); + } + } + + if (!e) { + e = bus_address_escape(host); + if (!e) + return -ENOMEM; + } + + b->address = strjoin("unixexec:path=ssh,argv1=-xT,argv2=", e, ",argv3=systemd-stdio-bridge", c, NULL); + if (!b->address) + return -ENOMEM; + + return 0; + } + +_public_ int sd_bus_open_system_remote(sd_bus **ret, const char *host) { + sd_bus *bus; + int r; + + assert_return(host, -EINVAL); + assert_return(ret, -EINVAL); + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + r = bus_set_address_system_remote(bus, host); + if (r < 0) + goto fail; + + bus->bus_client = true; + bus->trusted = false; + bus->is_system = true; + + r = sd_bus_start(bus); + if (r < 0) + goto fail; + + *ret = bus; + return 0; + +fail: + bus_free(bus); + return r; +} + +int bus_set_address_system_machine(sd_bus *b, const char *machine) { + _cleanup_free_ char *e = NULL; + + assert(b); + assert(machine); + + e = bus_address_escape(machine); + if (!e) + return -ENOMEM; + + b->address = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL); + if (!b->address) + return -ENOMEM; + + return 0; +} + +_public_ int sd_bus_open_system_machine(sd_bus **ret, const char *machine) { + sd_bus *bus; + int r; + + assert_return(machine, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(machine_name_is_valid(machine), -EINVAL); + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + r = bus_set_address_system_machine(bus, machine); + if (r < 0) + goto fail; + + bus->bus_client = true; + bus->trusted = false; + bus->is_system = true; + + r = sd_bus_start(bus); + if (r < 0) + goto fail; + + *ret = bus; + return 0; + +fail: + bus_free(bus); + return r; +} + +_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. */ +} + +_public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) { + + if (!bus) + return NULL; + + sd_bus_flush(bus); + sd_bus_close(bus); + + return sd_bus_unref(bus); +} + +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) { + + if (!bus) + return 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 > 0) + return NULL; + + 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 (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + + 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_bus_id(sd_bus *bus, sd_id128_t *id) { + int r; + + assert_return(bus, -EINVAL); + assert_return(id, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + r = bus_ensure_running(bus); + if (r < 0) + return r; + + *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) { + bool remarshal = false; + + assert(b); + + /* wrong packet version */ + if (b->message_version != 0 && b->message_version != (*m)->header->version) + remarshal = true; + + /* wrong packet endianness */ + if (b->message_endian != 0 && b->message_endian != (*m)->header->endian) + remarshal = true; + + /* TODO: kdbus-messages received from the kernel contain data which is + * not allowed to be passed to KDBUS_CMD_SEND. Therefore, we have to + * force remarshaling of the message. Technically, we could just + * recreate the kdbus message, but that is non-trivial as other parts of + * the message refer to m->kdbus already. This should be fixed! */ + if ((*m)->kdbus && (*m)->release_kdbus) + remarshal = true; + + return remarshal ? bus_message_remarshal(b, m) : 0; +} + +int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) { + assert(b); + assert(m); + + /* Fake some timestamps, if they were requested, and not + * already initialized */ + if (b->attach_flags & KDBUS_ATTACH_TIMESTAMP) { + if (m->realtime <= 0) + m->realtime = now(CLOCK_REALTIME); + + if (m->monotonic <= 0) + m->monotonic = now(CLOCK_MONOTONIC); + } + + /* 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, artificial 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, bool hint_sync_call, size_t *idx) { + int r; + + assert(bus); + assert(m); + + if (bus->is_kernel) + r = bus_kernel_write_message(bus, m, hint_sync_call); + 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=%" PRIu64 " reply_cookie=%" PRIu64 " 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)), + BUS_MESSAGE_COOKIE(m), + 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], false, &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, bool hint_priority, int64_t priority) { + assert(bus); + + if (bus->is_kernel) + return bus_kernel_read_message(bus, hint_priority, priority); + 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, bool hint_priority, int64_t priority, sd_bus_message **m) { + int r, ret = 0; + + assert(bus); + assert(m); + assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); + + /* Note that the priority logic is only available on kdbus, + * where the rqueue is unused. We check the rqueue here + * anyway, because it's simple... */ + + 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, hint_priority, priority); + if (r < 0) + return r; + if (r == 0) + return ret; + + ret = 1; + } +} + +static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, bool hint_sync_call) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); + int r; + + assert_return(m, -EINVAL); + + if (!bus) + bus = m->bus; + + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + 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 -EOPNOTSUPP; + } + + /* 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) + goto finish; + + if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) { + size_t idx = 0; + + r = bus_write_message(bus, m, hint_sync_call, &idx); + if (r < 0) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + bus_enter_closing(bus); + return -ECONNRESET; + } + + return r; + } + + 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); + } + +finish: + if (cookie) + *cookie = BUS_MESSAGE_COOKIE(m); + + return 1; +} + +_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) { + return bus_send_internal(bus, m, cookie, false); +} + +_public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie) { + int r; + + assert_return(m, -EINVAL); + + if (!bus) + bus = m->bus; + + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + 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_slot **slot, + sd_bus_message *_m, + sd_bus_message_handler_t callback, + void *userdata, + uint64_t usec) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); + _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *s = NULL; + int r; + + 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); + + if (!bus) + bus = m->bus; + + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + r = ordered_hashmap_ensure_allocated(&bus->reply_callbacks, &uint64_hash_ops); + 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; + + s = bus_slot_allocate(bus, !slot, BUS_REPLY_CALLBACK, sizeof(struct reply_callback), userdata); + if (!s) + return -ENOMEM; + + s->reply_callback.callback = callback; + + s->reply_callback.cookie = BUS_MESSAGE_COOKIE(m); + r = ordered_hashmap_put(bus->reply_callbacks, &s->reply_callback.cookie, &s->reply_callback); + if (r < 0) { + s->reply_callback.cookie = 0; + return r; + } + + s->reply_callback.timeout = calc_elapse(m->timeout); + if (s->reply_callback.timeout != 0) { + r = prioq_put(bus->reply_callbacks_prioq, &s->reply_callback, &s->reply_callback.prioq_idx); + if (r < 0) { + s->reply_callback.timeout = 0; + return r; + } + } + + r = sd_bus_send(bus, m, &s->reply_callback.cookie); + if (r < 0) + return r; + + if (slot) + *slot = s; + s = NULL; + + return r; +} + +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_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); + usec_t timeout; + uint64_t cookie; + unsigned i; + int r; + + bus_assert_return(m, -EINVAL, error); + bus_assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL, error); + bus_assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL, error); + bus_assert_return(!bus_error_is_dirty(error), -EINVAL, error); + + if (!bus) + bus = m->bus; + + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + bus_assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + r = bus_ensure_running(bus); + if (r < 0) + goto fail; + + i = bus->rqueue_size; + + r = bus_seal_message(bus, m, usec); + if (r < 0) + goto fail; + + r = bus_remarshal_message(bus, &m); + if (r < 0) + goto fail; + + r = bus_send_internal(bus, m, &cookie, true); + if (r < 0) + goto fail; + + 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--; + log_debug_bus_message(incoming); + + if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) { + + if (incoming->n_fds <= 0 || (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) { + if (reply) + *reply = incoming; + else + sd_bus_message_unref(incoming); + + return 1; + } + + r = sd_bus_error_setf(error, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptors which I couldn't accept. Sorry."); + sd_bus_message_unref(incoming); + return r; + + } else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR) { + r = sd_bus_error_copy(error, &incoming->error); + sd_bus_message_unref(incoming); + return r; + } else { + r = -EIO; + goto fail; + } + + } 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); + r = -ELOOP; + goto fail; + } + + /* Try to read more, right-away */ + i++; + } + + r = bus_read_message(bus, false, 0); + if (r < 0) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + bus_enter_closing(bus); + r = -ECONNRESET; + } + + goto fail; + } + if (r > 0) + continue; + + if (timeout > 0) { + usec_t n; + + n = now(CLOCK_MONOTONIC); + if (n >= timeout) { + r = -ETIMEDOUT; + goto fail; + } + + left = timeout - n; + } else + left = (uint64_t) -1; + + r = bus_poll(bus, true, left); + if (r < 0) + goto fail; + if (r == 0) { + r = -ETIMEDOUT; + goto fail; + } + + r = dispatch_wqueue(bus); + if (r < 0) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + bus_enter_closing(bus); + r = -ECONNRESET; + } + + goto fail; + } + } + +fail: + return sd_bus_error_set_errno(error, 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_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING) + return -ENOTCONN; + + 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_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING) + return -ENOTCONN; + + if (bus->track_queue) { + *timeout_usec = 0; + return 1; + } + + 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; + } + + if (c->timeout == 0) { + *timeout_usec = (uint64_t) -1; + return 0; + } + + *timeout_usec = c->timeout; + return 1; +} + +static int process_timeout(sd_bus *bus) { + _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message* m = NULL; + struct reply_callback *c; + sd_bus_slot *slot; + 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; + + r = bus_seal_synthetic_message(bus, m); + if (r < 0) + return r; + + assert_se(prioq_pop(bus->reply_callbacks_prioq) == c); + c->timeout = 0; + + ordered_hashmap_remove(bus->reply_callbacks, &c->cookie); + c->cookie = 0; + + slot = container_of(c, sd_bus_slot, reply_callback); + + bus->iteration_counter++; + + bus->current_message = m; + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = c->callback; + bus->current_userdata = slot->userdata; + r = c->callback(m, slot->userdata, &error_buffer); + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = NULL; + bus->current_message = NULL; + + if (slot->floating) { + bus_slot_disconnect(slot); + sd_bus_slot_unref(slot); + } + + sd_bus_slot_unref(slot); + + return bus_maybe_reply_error(m, r, &error_buffer); +} + +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 != 1) + return -EIO; + + return 0; +} + +static int process_reply(sd_bus *bus, sd_bus_message *m) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *synthetic_reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; + struct reply_callback *c; + sd_bus_slot *slot; + 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; + + if (bus->is_kernel && (bus->hello_flags & KDBUS_HELLO_MONITOR)) + return 0; + + if (m->destination && bus->unique_name && !streq_ptr(m->destination, bus->unique_name)) + return 0; + + c = ordered_hashmap_remove(bus->reply_callbacks, &m->reply_cookie); + if (!c) + return 0; + + c->cookie = 0; + + slot = container_of(c, sd_bus_slot, reply_callback); + + if (m->n_fds > 0 && !(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) { + + /* If the reply contained a file descriptor which we + * didn't want we pass an error instead. */ + + r = bus_message_new_synthetic_error( + bus, + m->reply_cookie, + &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptor"), + &synthetic_reply); + if (r < 0) + return r; + + /* Copy over original timestamp */ + synthetic_reply->realtime = m->realtime; + synthetic_reply->monotonic = m->monotonic; + synthetic_reply->seqnum = m->seqnum; + + r = bus_seal_synthetic_message(bus, synthetic_reply); + if (r < 0) + return r; + + m = synthetic_reply; + } else { + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + } + + if (c->timeout != 0) { + prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); + c->timeout = 0; + } + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = c->callback; + bus->current_userdata = slot->userdata; + r = c->callback(m, slot->userdata, &error_buffer); + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = NULL; + + if (slot->floating) { + bus_slot_disconnect(slot); + sd_bus_slot_unref(slot); + } + + sd_bus_slot_unref(slot); + + return bus_maybe_reply_error(m, r, &error_buffer); +} + +static int process_filter(sd_bus *bus, sd_bus_message *m) { + _cleanup_(sd_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) { + sd_bus_slot *slot; + + 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; + + slot = container_of(l, sd_bus_slot, filter_callback); + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = l->callback; + bus->current_userdata = slot->userdata; + r = l->callback(m, slot->userdata, &error_buffer); + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + + 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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(bus); + assert(m); + + if (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + + 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_fd_check(sd_bus *bus, sd_bus_message *m) { + assert(bus); + assert(m); + + /* If we got a message with a file descriptor which we didn't + * want to accept, then let's drop it. How can this even + * happen? For example, when the kernel queues a message into + * an activatable names's queue which allows fds, and then is + * delivered to us later even though we ourselves did not + * negotiate it. */ + + if (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + + if (m->n_fds <= 0) + return 0; + + if (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD) + return 0; + + if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) + return 1; /* just eat it up */ + + return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Message contains file descriptors, which I cannot accept. Sorry."); +} + +static int process_message(sd_bus *bus, sd_bus_message *m) { + int r; + + assert(bus); + assert(m); + + bus->current_message = m; + bus->iteration_counter++; + + log_debug_bus_message(m); + + r = process_hello(bus, m); + if (r != 0) + goto finish; + + r = process_reply(bus, m); + if (r != 0) + goto finish; + + r = process_fd_check(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_message = NULL; + return r; +} + +static int dispatch_track(sd_bus *bus) { + assert(bus); + + if (!bus->track_queue) + return 0; + + bus_track_dispatch(bus->track_queue); + return 1; +} + +static int process_running(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) { + _cleanup_(sd_bus_message_unrefp) 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_track(bus); + if (r != 0) + goto null_message; + + r = dispatch_rqueue(bus, hint_priority, priority, &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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct reply_callback *c; + int r; + + assert(bus); + assert(bus->state == BUS_CLOSING); + + c = ordered_hashmap_first(bus->reply_callbacks); + if (c) { + _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; + sd_bus_slot *slot; + + /* 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); + c->timeout = 0; + } + + ordered_hashmap_remove(bus->reply_callbacks, &c->cookie); + c->cookie = 0; + + slot = container_of(c, sd_bus_slot, reply_callback); + + bus->iteration_counter++; + + bus->current_message = m; + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = c->callback; + bus->current_userdata = slot->userdata; + r = c->callback(m, slot->userdata, &error_buffer); + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = NULL; + bus->current_message = NULL; + + if (slot->floating) { + bus_slot_disconnect(slot); + sd_bus_slot_unref(slot); + } + + sd_bus_slot_unref(slot); + + return bus_maybe_reply_error(m, r, &error_buffer); + } + + /* Then, synthesize a Disconnected message */ + r = sd_bus_message_new_signal( + bus, + &m, + "/org/freedesktop/DBus/Local", + "org.freedesktop.DBus.Local", + "Disconnected"); + if (r < 0) + return r; + + bus_message_set_sender_local(bus, m); + + r = bus_seal_synthetic_message(bus, m); + if (r < 0) + return r; + + sd_bus_close(bus); + + bus->current_message = 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_message = NULL; + + return r; +} + +static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priority, 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_message, -EBUSY); + assert(!bus->current_slot); + + switch (bus->state) { + + case BUS_UNSET: + return -ENOTCONN; + + case BUS_CLOSED: + return -ECONNRESET; + + case BUS_OPENING: + r = bus_socket_process_opening(bus); + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -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 (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -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, hint_priority, priority, ret); + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -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"); +} + +_public_ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { + return bus_process_internal(bus, false, 0, ret); +} + +_public_ int sd_bus_process_priority(sd_bus *bus, int64_t priority, sd_bus_message **ret) { + return bus_process_internal(bus, true, priority, ret); +} + +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_INFINITY; + + assert(bus); + + if (bus->state == BUS_CLOSING) + return 1; + + if (!BUS_IS_OPEN(bus->state)) + return -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; + + if (!BUS_IS_OPEN(bus->state)) + return -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; + + if (!BUS_IS_OPEN(bus->state)) + return -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 (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -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_slot **slot, + sd_bus_message_handler_t callback, + void *userdata) { + + sd_bus_slot *s; + + assert_return(bus, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + s = bus_slot_allocate(bus, !slot, BUS_FILTER_CALLBACK, sizeof(struct filter_callback), userdata); + if (!s) + return -ENOMEM; + + s->filter_callback.callback = callback; + + bus->filter_callbacks_modified = true; + LIST_PREPEND(callbacks, bus->filter_callbacks, &s->filter_callback); + + if (slot) + *slot = s; + + return 0; +} + +_public_ int sd_bus_add_match( + sd_bus *bus, + sd_bus_slot **slot, + const char *match, + sd_bus_message_handler_t callback, + void *userdata) { + + struct bus_match_component *components = NULL; + unsigned n_components = 0; + sd_bus_slot *s = NULL; + 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; + + s = bus_slot_allocate(bus, !slot, BUS_MATCH_CALLBACK, sizeof(struct match_callback), userdata); + if (!s) { + r = -ENOMEM; + goto finish; + } + + s->match_callback.callback = callback; + s->match_callback.cookie = ++bus->match_cookie; + + if (bus->bus_client) { + enum bus_match_scope scope; + + scope = bus_match_get_scope(components, n_components); + + /* Do not install server-side matches for matches + * against the local service, interface or bus + * path. */ + if (scope != BUS_MATCH_LOCAL) { + + if (!bus->is_kernel) { + /* When this is not a kernel transport, we + * store the original match string, so that we + * can use it to remove the match again */ + + s->match_callback.match_string = strdup(match); + if (!s->match_callback.match_string) { + r = -ENOMEM; + goto finish; + } + } + + r = bus_add_match_internal(bus, s->match_callback.match_string, components, n_components, s->match_callback.cookie); + if (r < 0) + goto finish; + + s->match_added = true; + } + } + + bus->match_callbacks_modified = true; + r = bus_match_add(&bus->match_callbacks, components, n_components, &s->match_callback); + if (r < 0) + goto finish; + + if (slot) + *slot = s; + s = NULL; + +finish: + bus_match_parse_free(components, n_components); + sd_bus_slot_unref(s); + + return r; +} + +int bus_remove_match_by_string( + sd_bus *bus, + const char *match, + sd_bus_message_handler_t callback, + void *userdata) { + + struct bus_match_component *components = NULL; + unsigned n_components = 0; + struct match_callback *c; + 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; + + r = bus_match_find(&bus->match_callbacks, components, n_components, NULL, NULL, &c); + if (r <= 0) + goto finish; + + sd_bus_slot_unref(container_of(c, sd_bus_slot, match_callback)); + +finish: + bus_match_parse_free(components, n_components); + + return r; +} + +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); + sd_bus_close(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_io_event_source, bus->input_fd, 0, io_callback, bus); + 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); + if (r < 0) + return r; + + r = sd_event_source_set_description(bus->input_io_event_source, "bus-input"); + } 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_io_event_source, bus->output_fd, 0, io_callback, bus); + if (r < 0) + return r; + + r = sd_event_source_set_priority(bus->output_io_event_source, bus->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(bus->input_io_event_source, "bus-output"); + } 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_time(bus->event, &bus->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, bus); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(bus->time_event_source, priority); + if (r < 0) + goto fail; + + r = sd_event_source_set_description(bus->time_event_source, "bus-time"); + if (r < 0) + goto fail; + + r = sd_event_add_exit(bus->event, &bus->quit_event_source, quit_callback, bus); + if (r < 0) + goto fail; + + r = sd_event_source_set_description(bus->quit_event_source, "bus-exit"); + 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); + } + + 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_message(sd_bus *bus) { + assert_return(bus, NULL); + + return bus->current_message; +} + +_public_ sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus) { + assert_return(bus, NULL); + + return bus->current_slot; +} + +_public_ sd_bus_message_handler_t sd_bus_get_current_handler(sd_bus *bus) { + assert_return(bus, NULL); + + return bus->current_handler; +} + +_public_ void* sd_bus_get_current_userdata(sd_bus *bus) { + assert_return(bus, NULL); + + return bus->current_userdata; +} + +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) { + return bus_default(sd_bus_open_system, &default_system_bus, ret); +} + + +_public_ int sd_bus_default_user(sd_bus **ret) { + return bus_default(sd_bus_open_user, &default_user_bus, ret); +} + +_public_ int sd_bus_default(sd_bus **ret) { + + const char *e; + + /* Let's try our best to reuse another cached connection. If + * the starter bus type is set, connect via our normal + * connection logic, ignoring $DBUS_STARTER_ADDRESS, so that + * we can share the connection with the user/system default + * bus. */ + + e = secure_getenv("DBUS_STARTER_BUS_TYPE"); + if (e) { + if (streq(e, "system")) + return sd_bus_default_system(ret); + else if (STR_IN_SET(e, "user", "session")) + return sd_bus_default_user(ret); + } + + /* No type is specified, so we have not other option than to + * use the starter address if it is set. */ + + e = secure_getenv("DBUS_STARTER_ADDRESS"); + if (e) { + + return bus_default(sd_bus_open, &default_starter_bus, ret); + } + + /* Finally, if nothing is set use the cached connection for + * the right scope */ + + if (cg_pid_get_owner_uid(0, NULL) >= 0) + return sd_bus_default_user(ret); + else + return sd_bus_default_system(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_ int sd_bus_path_encode(const char *prefix, const char *external_id, char **ret_path) { + _cleanup_free_ char *e = NULL; + char *ret; + + assert_return(object_path_is_valid(prefix), -EINVAL); + assert_return(external_id, -EINVAL); + assert_return(ret_path, -EINVAL); + + e = bus_label_escape(external_id); + if (!e) + return -ENOMEM; + + ret = strjoin(prefix, "/", e, NULL); + if (!ret) + return -ENOMEM; + + *ret_path = ret; + return 0; +} + +_public_ int sd_bus_path_decode(const char *path, const char *prefix, char **external_id) { + const char *e; + char *ret; + + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(object_path_is_valid(prefix), -EINVAL); + assert_return(external_id, -EINVAL); + + e = object_path_startswith(path, prefix); + if (!e) { + *external_id = NULL; + return 0; + } + + ret = bus_label_unescape(e); + if (!ret) + return -ENOMEM; + + *external_id = ret; + return 1; +} + +_public_ int sd_bus_path_encode_many(char **out, const char *path_template, ...) { + _cleanup_strv_free_ char **labels = NULL; + char *path, *path_pos, **label_pos; + const char *sep, *template_pos; + size_t path_length; + va_list list; + int r; + + assert_return(out, -EINVAL); + assert_return(path_template, -EINVAL); + + path_length = strlen(path_template); + + va_start(list, path_template); + for (sep = strchr(path_template, '%'); sep; sep = strchr(sep + 1, '%')) { + const char *arg; + char *label; + + arg = va_arg(list, const char *); + if (!arg) { + va_end(list); + return -EINVAL; + } + + label = bus_label_escape(arg); + if (!label) { + va_end(list); + return -ENOMEM; + } + + r = strv_consume(&labels, label); + if (r < 0) { + va_end(list); + return r; + } + + /* add label length, but account for the format character */ + path_length += strlen(label) - 1; + } + va_end(list); + + path = malloc(path_length + 1); + if (!path) + return -ENOMEM; + + path_pos = path; + label_pos = labels; + + for (template_pos = path_template; *template_pos; ) { + sep = strchrnul(template_pos, '%'); + path_pos = mempcpy(path_pos, template_pos, sep - template_pos); + if (!*sep) + break; + + path_pos = stpcpy(path_pos, *label_pos++); + template_pos = sep + 1; + } + + *path_pos = 0; + *out = path; + return 0; +} + +_public_ int sd_bus_path_decode_many(const char *path, const char *path_template, ...) { + _cleanup_strv_free_ char **labels = NULL; + const char *template_pos, *path_pos; + char **label_pos; + va_list list; + int r; + + /* + * This decodes an object-path based on a template argument. The + * template consists of a verbatim path, optionally including special + * directives: + * + * - Each occurrence of '%' in the template matches an arbitrary + * substring of a label in the given path. At most one such + * directive is allowed per label. For each such directive, the + * caller must provide an output parameter (char **) via va_arg. If + * NULL is passed, the given label is verified, but not returned. + * For each matched label, the *decoded* label is stored in the + * passed output argument, and the caller is responsible to free + * it. Note that the output arguments are only modified if the + * actualy path matched the template. Otherwise, they're left + * untouched. + * + * This function returns <0 on error, 0 if the path does not match the + * template, 1 if it matched. + */ + + assert_return(path, -EINVAL); + assert_return(path_template, -EINVAL); + + path_pos = path; + + for (template_pos = path_template; *template_pos; ) { + const char *sep; + size_t length; + char *label; + + /* verify everything until the next '%' matches verbatim */ + sep = strchrnul(template_pos, '%'); + length = sep - template_pos; + if (strncmp(path_pos, template_pos, length)) + return 0; + + path_pos += length; + template_pos += length; + + if (!*template_pos) + break; + + /* We found the next '%' character. Everything up until here + * matched. We now skip ahead to the end of this label and make + * sure it matches the tail of the label in the path. Then we + * decode the string in-between and save it for later use. */ + + ++template_pos; /* skip over '%' */ + + sep = strchrnul(template_pos, '/'); + length = sep - template_pos; /* length of suffix to match verbatim */ + + /* verify the suffixes match */ + sep = strchrnul(path_pos, '/'); + if (sep - path_pos < (ssize_t)length || + strncmp(sep - length, template_pos, length)) + return 0; + + template_pos += length; /* skip over matched label */ + length = sep - path_pos - length; /* length of sub-label to decode */ + + /* store unescaped label for later use */ + label = bus_label_unescape_n(path_pos, length); + if (!label) + return -ENOMEM; + + r = strv_consume(&labels, label); + if (r < 0) + return r; + + path_pos = sep; /* skip decoded label and suffix */ + } + + /* end of template must match end of path */ + if (*path_pos) + return 0; + + /* copy the labels over to the caller */ + va_start(list, path_template); + for (label_pos = labels; label_pos && *label_pos; ++label_pos) { + char **arg; + + arg = va_arg(list, char **); + if (arg) + *arg = *label_pos; + else + free(*label_pos); + } + va_end(list); + + free(labels); + labels = NULL; + return 1; +} + +_public_ int sd_bus_try_close(sd_bus *bus) { + int r; + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!bus->is_kernel) + return -EOPNOTSUPP; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + 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; +} + +_public_ int sd_bus_get_description(sd_bus *bus, const char **description) { + assert_return(bus, -EINVAL); + assert_return(description, -EINVAL); + assert_return(bus->description, -ENXIO); + assert_return(!bus_pid_changed(bus), -ECHILD); + + *description = bus->description; + return 0; +} + +int bus_get_root_path(sd_bus *bus) { + int r; + + if (bus->cgroup_root) + return 0; + + r = cg_get_root_path(&bus->cgroup_root); + if (r == -ENOENT) { + bus->cgroup_root = strdup("/"); + if (!bus->cgroup_root) + return -ENOMEM; + + r = 0; + } + + return r; +} + +_public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) { + int r; + + assert_return(bus, -EINVAL); + assert_return(scope, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (bus->is_kernel) { + _cleanup_free_ char *n = NULL; + const char *dash; + + r = bus_kernel_get_bus_name(bus, &n); + if (r < 0) + return r; + + if (streq(n, "0-system")) { + *scope = "system"; + return 0; + } + + dash = strchr(n, '-'); + if (streq_ptr(dash, "-user")) { + *scope = "user"; + return 0; + } + } + + if (bus->is_user) { + *scope = "user"; + return 0; + } + + if (bus->is_system) { + *scope = "system"; + return 0; + } + + return -ENODATA; +} + +_public_ int sd_bus_get_address(sd_bus *bus, const char **address) { + + assert_return(bus, -EINVAL); + assert_return(address, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (bus->address) { + *address = bus->address; + return 0; + } + + return -ENODATA; +} + +_public_ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) { + assert_return(bus, -EINVAL); + assert_return(mask, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + *mask = bus->creds_mask; + return 0; +} + +_public_ int sd_bus_is_bus_client(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->bus_client; +} + +_public_ int sd_bus_is_server(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->is_server; +} + +_public_ int sd_bus_is_anonymous(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->anonymous_auth; +} + +_public_ int sd_bus_is_trusted(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->trusted; +} + +_public_ int sd_bus_is_monitor(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return !!(bus->hello_flags & KDBUS_HELLO_MONITOR); +} + +static void flush_close(sd_bus *bus) { + if (!bus) + return; + + /* Flushes and closes the specified bus. We take a ref before, + * to ensure the flushing does not cause the bus to be + * unreferenced. */ + + sd_bus_flush_close_unref(sd_bus_ref(bus)); +} + +_public_ void sd_bus_default_flush_close(void) { + flush_close(default_starter_bus); + flush_close(default_user_bus); + flush_close(default_system_bus); +} diff --git a/src/libsystemd/src/sd-bus/test-bus-benchmark.c b/src/libsystemd/src/sd-bus/test-bus-benchmark.c new file mode 100644 index 0000000000..a222d36bb4 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-benchmark.c @@ -0,0 +1,371 @@ +/*** + 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 . +***/ + +#include + +#include + +#include "alloc-util.h" +#include "bus-internal.h" +#include "bus-kernel.h" +#include "bus-util.h" +#include "def.h" +#include "fd-util.h" +#include "time-util.h" +#include "util.h" + +#define MAX_SIZE (2*1024*1024) + +static usec_t arg_loop_usec = 100 * USEC_PER_MSEC; + +typedef enum Type { + TYPE_KDBUS, + TYPE_LEGACY, + TYPE_DIRECT, +} Type; + +static void server(sd_bus *b, size_t *result) { + int r; + + for (;;) { + _cleanup_(sd_bus_message_unrefp) 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_INFINITY) >= 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); + + r = sd_bus_reply_method_return(m, NULL); + assert_se(r >= 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 if (!sd_bus_message_is_signal(m, NULL, NULL)) + assert_not_reached("Unknown method"); + } +} + +static void transaction(sd_bus *b, size_t sz, const char *server_name) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + uint8_t *p; + + assert_se(sd_bus_message_new_method_call(b, &m, server_name, "/", "benchmark.server", "Work") >= 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, const char *server_name) { + _cleanup_(sd_bus_message_unrefp) 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); + + r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL); + assert_se(r >= 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, server_name); + 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, server_name); + 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, &x, server_name, "/", "benchmark.server", "Exit") >= 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(Type type, const char *address, const char *server_name, int fd) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL; + size_t csize; + sd_bus *b; + int r; + + r = sd_bus_new(&b); + assert_se(r >= 0); + + if (type == TYPE_DIRECT) { + r = sd_bus_set_fd(b, fd, fd); + assert_se(r >= 0); + } else { + r = sd_bus_set_address(b, address); + assert_se(r >= 0); + + r = sd_bus_set_bus_client(b, true); + assert_se(r >= 0); + } + + r = sd_bus_start(b); + assert_se(r >= 0); + + r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL); + assert_se(r >= 0); + + switch (type) { + case TYPE_KDBUS: + printf("SIZE\tCOPY\tMEMFD\n"); + break; + case TYPE_LEGACY: + printf("SIZE\tLEGACY\n"); + break; + case TYPE_DIRECT: + printf("SIZE\tDIRECT\n"); + break; + } + + for (csize = 1; csize <= MAX_SIZE; csize *= 2) { + usec_t t; + unsigned n_copying, n_memfd; + + printf("%zu\t", csize); + + if (type == TYPE_KDBUS) { + b->use_memfd = 0; + + t = now(CLOCK_MONOTONIC); + for (n_copying = 0;; n_copying++) { + transaction(b, csize, server_name); + 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, server_name); + 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, &x, server_name, "/", "benchmark.server", "Exit") >= 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; + Type type = TYPE_KDBUS; + int i, pair[2] = { -1, -1 }; + _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *server_name = NULL; + _cleanup_close_ int bus_ref = -1; + const char *unique; + 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; + } else if (streq(argv[i], "legacy")) { + type = TYPE_LEGACY; + continue; + } else if (streq(argv[i], "direct")) { + type = TYPE_DIRECT; + continue; + } + + assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0); + } + + assert_se(!MODE_BISECT || TYPE_KDBUS); + + assert_se(arg_loop_usec > 0); + + if (type == TYPE_KDBUS) { + 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); + } else if (type == TYPE_LEGACY) { + const char *e; + + e = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); + assert_se(e); + + address = strdup(e); + assert_se(address); + } + + r = sd_bus_new(&b); + assert_se(r >= 0); + + if (type == TYPE_DIRECT) { + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0); + + r = sd_bus_set_fd(b, pair[0], pair[0]); + assert_se(r >= 0); + + r = sd_bus_set_server(b, true, SD_ID128_NULL); + assert_se(r >= 0); + } else { + r = sd_bus_set_address(b, address); + assert_se(r >= 0); + + r = sd_bus_set_bus_client(b, true); + assert_se(r >= 0); + } + + r = sd_bus_start(b); + assert_se(r >= 0); + + if (type != TYPE_DIRECT) { + r = sd_bus_get_unique_name(b, &unique); + assert_se(r >= 0); + + server_name = strdup(unique); + assert_se(server_name); + } + + 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); + + safe_close(bus_ref); + sd_bus_unref(b); + + switch (mode) { + case MODE_BISECT: + client_bisect(address, server_name); + break; + + case MODE_CHART: + client_chart(type, address, server_name, pair[1]); + 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); + + safe_close(pair[1]); + sd_bus_unref(b); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-chat.c b/src/libsystemd/src/sd-bus/test-bus-chat.c new file mode 100644 index 0000000000..1f028d2b23 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-chat.c @@ -0,0 +1,560 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "bus-error.h" +#include "bus-internal.h" +#include "bus-match.h" +#include "bus-util.h" +#include "fd-util.h" +#include "formats-util.h" +#include "log.h" +#include "macro.h" +#include "util.h" + +static int match_callback(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_message *m, void *userdata, sd_bus_error *ret_error) { + int r; + + 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) + return log_error_errno(r, "Failed to send reply: %m"); + + 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_se(_bus); + + r = sd_bus_open_user(&bus); + if (r < 0) { + log_error_errno(r, "Failed to connect to user bus: %m"); + goto fail; + } + + r = sd_bus_get_bus_id(bus, &id); + if (r < 0) { + log_error_errno(r, "Failed to get server ID: %m"); + goto fail; + } + + r = sd_bus_get_unique_name(bus, &unique); + if (r < 0) { + log_error_errno(r, "Failed to get unique name: %m"); + 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_errno(r, "Failed to acquire name: %m"); + goto fail; + } + + r = sd_bus_add_fallback(bus, NULL, "/foo/bar", object_callback, NULL); + if (r < 0) { + log_error_errno(r, "Failed to add object: %m"); + goto fail; + } + + r = sd_bus_add_match(bus, NULL, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL); + if (r < 0) { + log_error_errno(r, "Failed to add match: %m"); + goto fail; + } + + r = sd_bus_add_match(bus, NULL, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL); + if (r < 0) { + log_error_errno(r, "Failed to add match: %m"); + goto fail; + } + + bus_match_dump(&bus->match_callbacks, 0); + + *_bus = bus; + return 0; + +fail: + 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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + pid_t pid = 0; + const char *label = NULL; + + r = sd_bus_process(bus, &m); + if (r < 0) { + log_error_errno(r, "Failed to process requests: %m"); + goto fail; + } + + if (r == 0) { + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) { + log_error_errno(r, "Failed to wait: %m"); + 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="PID_FMT" label=%s", + strna(sd_bus_message_get_member(m)), + 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_errno(r, "Failed to get parameter: %m"); + 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_errno(r, "Failed to send reply: %m"); + 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_errno(r, "Failed to send reply: %m"); + 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_errno(r, "Failed to send reply: %m"); + 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_errno(r, "Failed to send reply: %m"); + 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_errno(r, "Failed to get parameter: %m"); + goto fail; + } + + log_info("Received fd=%d", fd); + + if (write(fd, &x, 1) < 0) { + log_error_errno(errno, "Failed to write to fd: %m"); + safe_close(fd); + goto fail; + } + + r = sd_bus_reply_method_return(m, NULL); + if (r < 0) { + log_error_errno(r, "Failed to send reply: %m"); + 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_errno(r, "Failed to send reply: %m"); + goto fail; + } + } + } + + r = 0; + +fail: + if (bus) { + sd_bus_flush(bus); + sd_bus_unref(bus); + } + + return r; +} + +static void* client1(void*p) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const char *hello; + int r; + _cleanup_close_pair_ int pp[2] = { -1, -1 }; + char x; + + r = sd_bus_open_user(&bus); + if (r < 0) { + log_error_errno(r, "Failed to connect to user bus: %m"); + 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_errno(r, "Failed to issue method call: %m"); + goto finish; + } + + r = sd_bus_message_read(reply, "s", &hello); + if (r < 0) { + log_error_errno(r, "Failed to get string: %m"); + goto finish; + } + + assert_se(streq(hello, "hello")); + + if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) { + log_error_errno(errno, "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_errno(r, "Failed to issue method call: %m"); + 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_(sd_bus_message_unrefp) sd_bus_message *q; + + r = sd_bus_message_new_method_call( + bus, + &q, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.systemd.test", + "ExitClient1"); + if (r < 0) + log_error_errno(r, "Failed to allocate method call: %m"); + else + sd_bus_send(bus, q, NULL); + + } + + return INT_TO_PTR(r); +} + +static int quit_callback(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_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_bus_error_free) 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_errno(r, "Failed to connect to user bus: %m"); + goto finish; + } + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd.test", + "/foo/bar/waldo/piep", + "org.object.test", + "Foobar"); + if (r < 0) { + log_error_errno(r, "Failed to allocate method call: %m"); + 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; + } + + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_signal( + bus, + &m, + "/foobar", + "foo.bar", + "Notify"); + if (r < 0) { + log_error_errno(r, "Failed to allocate signal: %m"); + 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; + } + + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.DBus.Peer", + "GetMachineId"); + if (r < 0) { + log_error_errno(r, "Failed to allocate method call: %m"); + 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_errno(r, "Failed to parse machine ID: %m"); + goto finish; + } + + log_info("Machine ID is %s.", mid); + + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.systemd.test", + "Slow"); + if (r < 0) { + log_error_errno(r, "Failed to allocate method call: %m"); + goto finish; + } + + reply = sd_bus_message_unref(reply); + + 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."); + + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.systemd.test", + "Slow"); + if (r < 0) { + log_error_errno(r, "Failed to allocate method call: %m"); + goto finish; + } + + r = sd_bus_call_async(bus, NULL, m, quit_callback, &quit, 200 * USEC_PER_MSEC); + 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_errno(r, "Failed to process requests: %m"); + goto finish; + } + if (r == 0) { + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) { + log_error_errno(r, "Failed to wait: %m"); + goto finish; + } + } + } + + r = 0; + +finish: + if (bus) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *q; + + r = sd_bus_message_new_method_call( + bus, + &q, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.systemd.test", + "ExitClient2"); + if (r < 0) { + log_error_errno(r, "Failed to allocate method call: %m"); + goto finish; + } + + (void) sd_bus_send(bus, q, NULL); + } + + 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/src/sd-bus/test-bus-cleanup.c b/src/libsystemd/src/sd-bus/test-bus-cleanup.c new file mode 100644 index 0000000000..bd4a3fbf34 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-cleanup.c @@ -0,0 +1,95 @@ +/*** + 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 . +***/ + +#include + +#include + +#include "bus-internal.h" +#include "bus-message.h" +#include "bus-util.h" +#include "refcnt.h" + +static void test_bus_new(void) { + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + + assert_se(sd_bus_new(&bus) == 0); + printf("after new: refcount %u\n", REFCNT_GET(bus->n_ref)); +} + +static int test_bus_open(void) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + r = sd_bus_open_system(&bus); + if (r == -ECONNREFUSED || r == -ENOENT) + return r; + + assert_se(r >= 0); + printf("after open: refcount %u\n", REFCNT_GET(bus->n_ref)); + + return 0; +} + +static void test_bus_new_method_call(void) { + sd_bus *bus = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + + assert_se(sd_bus_open_system(&bus) >= 0); + + assert_se(sd_bus_message_new_method_call(bus, &m, "a.service.name", "/an/object/path", "an.interface.name", "AMethodName") >= 0); + + printf("after message_new_method_call: refcount %u\n", REFCNT_GET(bus->n_ref)); + + sd_bus_flush_close_unref(bus); + printf("after bus_flush_close_unref: refcount %u\n", m->n_ref); +} + +static void test_bus_new_signal(void) { + sd_bus *bus = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + + assert_se(sd_bus_open_system(&bus) >= 0); + + assert_se(sd_bus_message_new_signal(bus, &m, "/an/object/path", "an.interface.name", "Name") >= 0); + + printf("after message_new_signal: refcount %u\n", REFCNT_GET(bus->n_ref)); + + sd_bus_flush_close_unref(bus); + printf("after bus_flush_close_unref: refcount %u\n", m->n_ref); +} + +int main(int argc, char **argv) { + int r; + + log_parse_environment(); + log_open(); + + test_bus_new(); + r = test_bus_open(); + if (r < 0) { + log_info("Failed to connect to bus, skipping tests."); + return EXIT_TEST_SKIP; + } + + test_bus_new_method_call(); + test_bus_new_signal(); + + return EXIT_SUCCESS; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-creds.c b/src/libsystemd/src/sd-bus/test-bus-creds.c new file mode 100644 index 0000000000..c58b76c258 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-creds.c @@ -0,0 +1,50 @@ +/*** + 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 . +***/ + +#include + +#include "bus-dump.h" +#include "bus-util.h" +#include "cgroup-util.h" + +int main(int argc, char *argv[]) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + int r; + + if (cg_unified() == -ENOMEDIUM) { + puts("Skipping test: /sys/fs/cgroup/ not available"); + return EXIT_TEST_SKIP; + } + + r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL); + assert_se(r >= 0); + + bus_creds_dump(creds, NULL, true); + + creds = sd_bus_creds_unref(creds); + + r = sd_bus_creds_new_from_pid(&creds, 1, _SD_BUS_CREDS_ALL); + if (r != -EACCES) { + assert_se(r >= 0); + putchar('\n'); + bus_creds_dump(creds, NULL, true); + } + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-error.c b/src/libsystemd/src/sd-bus/test-bus-error.c new file mode 100644 index 0000000000..bce3cc31c9 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-error.c @@ -0,0 +1,232 @@ +/*** + 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 . +***/ + +#include + +#include "bus-common-errors.h" +#include "bus-error.h" +#include "bus-util.h" +#include "errno-list.h" + +static void test_error(void) { + _cleanup_(sd_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") == -EOPNOTSUPP); + 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) == EOPNOTSUPP); + assert_se(sd_bus_error_is_set(&error)); + sd_bus_error_free(&error); + + /* Check with no error */ + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_setf(&error, NULL, "yyy %i", -1) == 0); + assert_se(error.name == NULL); + assert_se(error.message == NULL); + assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND)); + assert_se(sd_bus_error_get_errno(&error) == 0); + 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)); + sd_bus_error_free(&error); + + /* Check with no error */ + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_set_errnof(&error, 0, "Waldi %c", 'X') == 0); + assert_se(error.name == NULL); + assert_se(error.message == NULL); + assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR)); + assert_se(sd_bus_error_get_errno(&error) == 0); + assert_se(!sd_bus_error_is_set(&error)); +} + +extern const sd_bus_error_map __start_BUS_ERROR_MAP[]; +extern const sd_bus_error_map __stop_BUS_ERROR_MAP[]; + +static void dump_mapping_table(void) { + const sd_bus_error_map *m; + + printf("----- errno mappings ------\n"); + m = __start_BUS_ERROR_MAP; + while (m < __stop_BUS_ERROR_MAP) { + + if (m->code == BUS_ERROR_MAP_END_MARKER) { + m = ALIGN8_PTR(m+1); + continue; + } + + printf("%s -> %i/%s\n", strna(m->name), m->code, strna(errno_to_name(m->code))); + m++; + } + printf("---------------------------\n"); +} + +static void test_errno_mapping_standard(void) { + assert_se(sd_bus_error_set(NULL, "System.Error.EUCLEAN", NULL) == -EUCLEAN); + assert_se(sd_bus_error_set(NULL, "System.Error.EBUSY", NULL) == -EBUSY); + assert_se(sd_bus_error_set(NULL, "System.Error.EINVAL", NULL) == -EINVAL); + assert_se(sd_bus_error_set(NULL, "System.Error.WHATSIT", NULL) == -EIO); +} + +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error", 5), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-2", 52), + SD_BUS_ERROR_MAP_END +}; + +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors2[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-3", 33), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-4", 44), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-33", 333), + SD_BUS_ERROR_MAP_END +}; + +static const sd_bus_error_map test_errors3[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-88", 888), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-99", 999), + SD_BUS_ERROR_MAP_END +}; + +static const sd_bus_error_map test_errors4[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-77", 777), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-78", 778), + SD_BUS_ERROR_MAP_END +}; + +static const sd_bus_error_map test_errors_bad1[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-1", 0), + SD_BUS_ERROR_MAP_END +}; + +static const sd_bus_error_map test_errors_bad2[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-1", -1), + SD_BUS_ERROR_MAP_END +}; + +static void test_errno_mapping_custom(void) { + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error", NULL) == -5); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-x", NULL) == -EIO); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-33", NULL) == -333); + + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -EIO); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -EIO); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -EIO); + + assert_se(sd_bus_error_add_map(test_errors3) > 0); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -888); + assert_se(sd_bus_error_add_map(test_errors4) > 0); + assert_se(sd_bus_error_add_map(test_errors4) == 0); + assert_se(sd_bus_error_add_map(test_errors3) == 0); + + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -999); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -777); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-78", NULL) == -778); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-y", NULL) == -EIO); + + assert_se(sd_bus_error_set(NULL, BUS_ERROR_NO_SUCH_UNIT, NULL) == -ENOENT); + + assert_se(sd_bus_error_add_map(test_errors_bad1) == -EINVAL); + assert_se(sd_bus_error_add_map(test_errors_bad2) == -EINVAL); +} + +int main(int argc, char *argv[]) { + dump_mapping_table(); + + test_error(); + test_errno_mapping_standard(); + test_errno_mapping_custom(); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-gvariant.c b/src/libsystemd/src/sd-bus/test-bus-gvariant.c new file mode 100644 index 0000000000..3c9ec9fef0 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-gvariant.c @@ -0,0 +1,224 @@ +/*** + 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 . +***/ + +#ifdef HAVE_GLIB +#include +#endif + +#include + +#include "alloc-util.h" +#include "bus-dump.h" +#include "bus-gvariant.h" +#include "bus-internal.h" +#include "bus-message.h" +#include "bus-util.h" +#include "macro.h" +#include "util.h" + +static void test_bus_gvariant_is_fixed_size(void) { + assert_se(bus_gvariant_is_fixed_size("") > 0); + assert_se(bus_gvariant_is_fixed_size("()") > 0); + assert_se(bus_gvariant_is_fixed_size("y") > 0); + assert_se(bus_gvariant_is_fixed_size("u") > 0); + assert_se(bus_gvariant_is_fixed_size("b") > 0); + assert_se(bus_gvariant_is_fixed_size("n") > 0); + assert_se(bus_gvariant_is_fixed_size("q") > 0); + assert_se(bus_gvariant_is_fixed_size("i") > 0); + assert_se(bus_gvariant_is_fixed_size("t") > 0); + assert_se(bus_gvariant_is_fixed_size("d") > 0); + assert_se(bus_gvariant_is_fixed_size("s") == 0); + assert_se(bus_gvariant_is_fixed_size("o") == 0); + assert_se(bus_gvariant_is_fixed_size("g") == 0); + assert_se(bus_gvariant_is_fixed_size("h") > 0); + assert_se(bus_gvariant_is_fixed_size("ay") == 0); + assert_se(bus_gvariant_is_fixed_size("v") == 0); + assert_se(bus_gvariant_is_fixed_size("(u)") > 0); + assert_se(bus_gvariant_is_fixed_size("(uuuuy)") > 0); + assert_se(bus_gvariant_is_fixed_size("(uusuuy)") == 0); + assert_se(bus_gvariant_is_fixed_size("a{ss}") == 0); + assert_se(bus_gvariant_is_fixed_size("((u)yyy(b(iiii)))") > 0); + assert_se(bus_gvariant_is_fixed_size("((u)yyy(b(iiivi)))") == 0); +} + +static void test_bus_gvariant_get_size(void) { + assert_se(bus_gvariant_get_size("") == 0); + assert_se(bus_gvariant_get_size("()") == 1); + assert_se(bus_gvariant_get_size("y") == 1); + assert_se(bus_gvariant_get_size("u") == 4); + assert_se(bus_gvariant_get_size("b") == 1); + assert_se(bus_gvariant_get_size("n") == 2); + assert_se(bus_gvariant_get_size("q") == 2); + assert_se(bus_gvariant_get_size("i") == 4); + assert_se(bus_gvariant_get_size("t") == 8); + assert_se(bus_gvariant_get_size("d") == 8); + assert_se(bus_gvariant_get_size("s") < 0); + assert_se(bus_gvariant_get_size("o") < 0); + assert_se(bus_gvariant_get_size("g") < 0); + assert_se(bus_gvariant_get_size("h") == 4); + assert_se(bus_gvariant_get_size("ay") < 0); + assert_se(bus_gvariant_get_size("v") < 0); + assert_se(bus_gvariant_get_size("(u)") == 4); + assert_se(bus_gvariant_get_size("(uuuuy)") == 20); + assert_se(bus_gvariant_get_size("(uusuuy)") < 0); + assert_se(bus_gvariant_get_size("a{ss}") < 0); + assert_se(bus_gvariant_get_size("((u)yyy(b(iiii)))") == 28); + assert_se(bus_gvariant_get_size("((u)yyy(b(iiivi)))") < 0); + assert_se(bus_gvariant_get_size("((b)(t))") == 16); + assert_se(bus_gvariant_get_size("((b)(b)(t))") == 16); + assert_se(bus_gvariant_get_size("(bt)") == 16); + assert_se(bus_gvariant_get_size("((t)(b))") == 16); + assert_se(bus_gvariant_get_size("(tb)") == 16); + assert_se(bus_gvariant_get_size("((b)(b))") == 2); + assert_se(bus_gvariant_get_size("((t)(t))") == 16); +} + +static void test_bus_gvariant_get_alignment(void) { + assert_se(bus_gvariant_get_alignment("") == 1); + assert_se(bus_gvariant_get_alignment("()") == 1); + assert_se(bus_gvariant_get_alignment("y") == 1); + assert_se(bus_gvariant_get_alignment("b") == 1); + assert_se(bus_gvariant_get_alignment("u") == 4); + assert_se(bus_gvariant_get_alignment("s") == 1); + assert_se(bus_gvariant_get_alignment("o") == 1); + assert_se(bus_gvariant_get_alignment("g") == 1); + assert_se(bus_gvariant_get_alignment("v") == 8); + assert_se(bus_gvariant_get_alignment("h") == 4); + assert_se(bus_gvariant_get_alignment("i") == 4); + assert_se(bus_gvariant_get_alignment("t") == 8); + assert_se(bus_gvariant_get_alignment("x") == 8); + assert_se(bus_gvariant_get_alignment("q") == 2); + assert_se(bus_gvariant_get_alignment("n") == 2); + assert_se(bus_gvariant_get_alignment("d") == 8); + assert_se(bus_gvariant_get_alignment("ay") == 1); + assert_se(bus_gvariant_get_alignment("as") == 1); + assert_se(bus_gvariant_get_alignment("au") == 4); + assert_se(bus_gvariant_get_alignment("an") == 2); + assert_se(bus_gvariant_get_alignment("ans") == 2); + assert_se(bus_gvariant_get_alignment("ant") == 8); + assert_se(bus_gvariant_get_alignment("(ss)") == 1); + assert_se(bus_gvariant_get_alignment("(ssu)") == 4); + assert_se(bus_gvariant_get_alignment("a(ssu)") == 4); + assert_se(bus_gvariant_get_alignment("(u)") == 4); + assert_se(bus_gvariant_get_alignment("(uuuuy)") == 4); + assert_se(bus_gvariant_get_alignment("(uusuuy)") == 4); + assert_se(bus_gvariant_get_alignment("a{ss}") == 1); + assert_se(bus_gvariant_get_alignment("((u)yyy(b(iiii)))") == 4); + assert_se(bus_gvariant_get_alignment("((u)yyy(b(iiivi)))") == 8); + assert_se(bus_gvariant_get_alignment("((b)(t))") == 8); + assert_se(bus_gvariant_get_alignment("((b)(b)(t))") == 8); + assert_se(bus_gvariant_get_alignment("(bt)") == 8); + assert_se(bus_gvariant_get_alignment("((t)(b))") == 8); + assert_se(bus_gvariant_get_alignment("(tb)") == 8); + assert_se(bus_gvariant_get_alignment("((b)(b))") == 1); + assert_se(bus_gvariant_get_alignment("((t)(t))") == 8); +} + +static void test_marshal(void) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *n = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_free_ void *blob; + size_t sz; + int r; + + r = sd_bus_open_system(&bus); + if (r < 0) + exit(EXIT_TEST_SKIP); + + bus->message_version = 2; /* dirty hack to enable gvariant */ + + assert_se(sd_bus_message_new_method_call(bus, &m, "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") >= 0); + + assert_cc(sizeof(struct bus_header) == 16); + + 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("(yyyyuta{tv})"), m->header, sizeof(struct bus_header) + m->fields_size, false, NULL, NULL); + assert_se(g_variant_is_normal_form(v)); + 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, m->user_body_size, false, NULL, NULL); + assert_se(g_variant_is_normal_form(v)); + 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, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); + + assert_se(bus_message_get_blob(m, &blob, &sz) >= 0); + +#ifdef HAVE_GLIB + { + GVariant *v; + char *t; + + v = g_variant_new_from_data(G_VARIANT_TYPE("(yyyyuta{tv}v)"), blob, sz, false, NULL, NULL); + assert_se(g_variant_is_normal_form(v)); + t = g_variant_print(v, TRUE); + printf("%s\n", t); + g_free(t); + g_variant_unref(v); + } +#endif + + assert_se(bus_message_from_malloc(bus, blob, sz, NULL, 0, NULL, &n) >= 0); + blob = NULL; + + assert_se(bus_message_dump(n, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); + + m = sd_bus_message_unref(m); + + assert_se(sd_bus_message_new_method_call(bus, &m, "a.x", "/a/x", "a.x", "Ax") >= 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, BUS_MESSAGE_DUMP_WITH_HEADER) >= 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/src/sd-bus/test-bus-introspect.c b/src/libsystemd/src/sd-bus/test-bus-introspect.c new file mode 100644 index 0000000000..4425cfae26 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-introspect.c @@ -0,0 +1,63 @@ +/*** + 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 . +***/ + +#include "bus-introspect.h" +#include "log.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_PROPERTY_EXPLICIT), + 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, " \n"); + assert_se(introspect_write_interface(&intro, vtable) >= 0); + fputs(" \n", intro.f); + + fflush(intro.f); + fputs(intro.introspection, stdout); + + introspect_free(&intro); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-kernel-bloom.c b/src/libsystemd/src/sd-bus/test-bus-kernel-bloom.c new file mode 100644 index 0000000000..f16e14a310 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-kernel-bloom.c @@ -0,0 +1,141 @@ +/*** + 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 . +***/ + +#include + +#include "alloc-util.h" +#include "bus-kernel.h" +#include "bus-util.h" +#include "fd-util.h" +#include "log.h" +#include "util.h" + +static int test_match(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + int *found = userdata; + + *found = 1; + + return 0; +} + +static void test_one( + const char *path, + const char *interface, + const char *member, + bool as_list, + 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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + sd_bus *a, *b; + int r, found = 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(&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, NULL, match, test_match, &found); + assert_se(r >= 0); + + log_debug("signal"); + + if (as_list) + r = sd_bus_emit_signal(a, path, interface, member, "as", 1, arg0); + else + 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 == !!found); + + 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", false, "foobar", "", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/tuut'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "interface='waldo.com'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "member='Piep'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "member='Pi_ep'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "arg0='foobar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "arg0='foo_bar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foobar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foo_bar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0has='foobar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0has='foo_bar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar2'", false); + + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/quux'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar/waldo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/quux'", false); + test_one("/", "waldo.com", "Piep", false, "foobar", "path_namespace='/'", true); + + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar/waldo/'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/'", true); + + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo/bar/waldo", "arg0path='/foo/'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo", "arg0path='/foo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo", "arg0path='/foo/bar/waldo'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo/", "arg0path='/foo/bar/waldo'", true); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-kernel.c b/src/libsystemd/src/sd-bus/test-bus-kernel.c new file mode 100644 index 0000000000..2a5ba60cc9 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-kernel.c @@ -0,0 +1,190 @@ +/*** + 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 . +***/ + +#include + +#include + +#include "alloc-util.h" +#include "bus-dump.h" +#include "bus-kernel.h" +#include "bus-util.h" +#include "fd-util.h" +#include "log.h" +#include "util.h" + +int main(int argc, char *argv[]) { + _cleanup_close_ int bus_ref = -1; + _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *bname = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_(sd_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]; + const char *nn; + + 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_description(a, "a"); + 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_timestamp(a, 1) >= 0); + assert_se(sd_bus_negotiate_creds(a, true, _SD_BUS_CREDS_ALL) >= 0); + + assert_se(sd_bus_negotiate_timestamp(b, 0) >= 0); + assert_se(sd_bus_negotiate_creds(b, true, 0) >= 0); + + r = sd_bus_start(a); + assert_se(r >= 0); + + r = sd_bus_start(b); + assert_se(r >= 0); + + assert_se(sd_bus_negotiate_timestamp(b, 1) >= 0); + assert_se(sd_bus_negotiate_creds(b, true, _SD_BUS_CREDS_ALL) >= 0); + + r = sd_bus_get_unique_name(a, &ua); + assert_se(r >= 0); + printf("unique a: %s\n", ua); + + r = sd_bus_get_description(a, &nn); + assert_se(r >= 0); + printf("name of a: %s\n", nn); + + r = sd_bus_get_unique_name(b, &ub); + assert_se(r >= 0); + printf("unique b: %s\n", ub); + + r = sd_bus_get_description(b, &nn); + assert_se(r >= 0); + printf("name of b: %s\n", nn); + + assert_se(bus_kernel_get_bus_name(b, &bname) >= 0); + assert_se(endswith(bname, name)); + + 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, NULL, "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_priority(b, -10, &m); + assert_se(r == 0); + + r = sd_bus_process(b, &m); + assert_se(r > 0); + assert_se(m); + + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + 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, &m, "net.x0pointer.foobar", "/a/path", "an.inter.face", "AMethod"); + assert_se(r >= 0); + + assert_se(pipe2(pipe_fds, O_CLOEXEC) >= 0); + + assert_se(write(pipe_fds[1], "x", 1) == 1); + + pipe_fds[1] = safe_close(pipe_fds[1]); + + r = sd_bus_message_append(m, "h", pipe_fds[0]); + assert_se(r >= 0); + + pipe_fds[0] = safe_close(pipe_fds[0]); + + 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, BUS_MESSAGE_DUMP_WITH_HEADER); + 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/src/sd-bus/test-bus-marshal.c b/src/libsystemd/src/sd-bus/test-bus-marshal.c new file mode 100644 index 0000000000..45db4764a0 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-marshal.c @@ -0,0 +1,432 @@ +/*** + 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 . +***/ + +#include +#include + +#ifdef HAVE_GLIB +#include +#endif + +#ifdef HAVE_DBUS +#include +#endif + +#include + +#include "alloc-util.h" +#include "bus-dump.h" +#include "bus-label.h" +#include "bus-message.h" +#include "bus-util.h" +#include "fd-util.h" +#include "hexdecoct.h" +#include "log.h" +#include "util.h" + +static void test_bus_path_encode_unique(void) { + _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL; + + assert_se(bus_path_encode_unique(NULL, "/foo/bar", "some.sender", "a.suffix", &a) >= 0 && streq_ptr(a, "/foo/bar/some_2esender/a_2esuffix")); + assert_se(bus_path_decode_unique(a, "/foo/bar", &b, &c) > 0 && streq_ptr(b, "some.sender") && streq_ptr(c, "a.suffix")); + assert_se(bus_path_decode_unique(a, "/bar/foo", &d, &d) == 0 && !d); + assert_se(bus_path_decode_unique("/foo/bar/onlyOneSuffix", "/foo/bar", &d, &d) == 0 && !d); + assert_se(bus_path_decode_unique("/foo/bar/_/_", "/foo/bar", &d, &e) > 0 && streq_ptr(d, "") && streq_ptr(e, "")); +} + +static void test_bus_path_encode(void) { + _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *f = NULL; + + assert_se(sd_bus_path_encode("/foo/bar", "waldo", &a) >= 0 && streq(a, "/foo/bar/waldo")); + assert_se(sd_bus_path_decode(a, "/waldo", &b) == 0 && b == NULL); + assert_se(sd_bus_path_decode(a, "/foo/bar", &b) > 0 && streq(b, "waldo")); + + assert_se(sd_bus_path_encode("xxxx", "waldo", &c) < 0); + assert_se(sd_bus_path_encode("/foo/", "waldo", &c) < 0); + + assert_se(sd_bus_path_encode("/foo/bar", "", &c) >= 0 && streq(c, "/foo/bar/_")); + assert_se(sd_bus_path_decode(c, "/foo/bar", &d) > 0 && streq(d, "")); + + assert_se(sd_bus_path_encode("/foo/bar", "foo.bar", &e) >= 0 && streq(e, "/foo/bar/foo_2ebar")); + assert_se(sd_bus_path_decode(e, "/foo/bar", &f) > 0 && streq(f, "foo.bar")); +} + +static void test_bus_path_encode_many(void) { + _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *f = NULL; + + assert_se(sd_bus_path_decode_many("/foo/bar", "/prefix/%", NULL) == 0); + assert_se(sd_bus_path_decode_many("/prefix/bar", "/prefix/%bar", NULL) == 1); + assert_se(sd_bus_path_decode_many("/foo/bar", "/prefix/%/suffix", NULL) == 0); + assert_se(sd_bus_path_decode_many("/prefix/foobar/suffix", "/prefix/%/suffix", &a) == 1 && streq_ptr(a, "foobar")); + assert_se(sd_bus_path_decode_many("/prefix/one_foo_two/mid/three_bar_four/suffix", "/prefix/one_%_two/mid/three_%_four/suffix", &b, &c) == 1 && streq_ptr(b, "foo") && streq_ptr(c, "bar")); + assert_se(sd_bus_path_decode_many("/prefix/one_foo_two/mid/three_bar_four/suffix", "/prefix/one_%_two/mid/three_%_four/suffix", NULL, &d) == 1 && streq_ptr(d, "bar")); + + assert_se(sd_bus_path_decode_many("/foo/bar", "/foo/bar/%", NULL) == 0); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/bar%", NULL) == 0); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/bar", NULL) == 0); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%bar", NULL) == 0); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/bar/suffix") == 1); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%%/suffix", NULL, NULL) == 0); /* multiple '%' are treated verbatim */ + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/suffi", NULL) == 0); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/suffix", &e) == 1 && streq_ptr(e, "bar")); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/%", NULL, NULL) == 1); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%/%", NULL, NULL, NULL) == 1); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "%/%/%", NULL, NULL, NULL) == 0); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%", NULL, NULL) == 0); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%/", NULL, NULL) == 0); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/", NULL) == 0); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%", NULL) == 0); + assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "%", NULL) == 0); + + assert_se(sd_bus_path_encode_many(&f, "/prefix/one_%_two/mid/three_%_four/suffix", "foo", "bar") >= 0 && streq_ptr(f, "/prefix/one_foo_two/mid/three_bar_four/suffix")); +} + +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 = bus_label_escape(a)); + assert_se(streq(t, b)); + + assert_se(x = bus_label_unescape(t)); + assert_se(streq(a, x)); + + assert_se(y = 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_(sd_bus_message_unrefp) 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; + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + double dbl; + uint64_t u64; + + r = sd_bus_default_system(&bus); + if (r < 0) + return EXIT_TEST_SKIP; + + r = sd_bus_message_new_method_call(bus, &m, "foobar.waldo", "/", "foobar.waldo", "Piep"); + assert_se(r >= 0); + + r = sd_bus_message_append(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, "y(ty)y(yt)y", 8, 777ULL, 7, 9, 77, 7777ULL, 10); + assert_se(r >= 0); + + r = sd_bus_message_append(m, "()"); + 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 = sd_bus_message_append(m, "a(stdo)", 1, "foo", 815ULL, 47.0, "/"); + assert_se(r >= 0); + + r = bus_message_seal(m, 4711, 0); + assert_se(r >= 0); + + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + ms = open_memstream(&first, &first_size); + bus_message_dump(m, ms, 0); + 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 = %zu, contents =\n%s", 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); + + dbus_error_free(&error); + } +#endif + + m = sd_bus_message_unref(m); + + r = bus_message_from_malloc(bus, buffer, sz, NULL, 0, NULL, &m); + assert_se(r >= 0); + + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + fclose(ms); + ms = open_memstream(&second, &second_size); + bus_message_dump(m, ms, 0); + 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, "y(ty)", &v, &u64, &u); + assert_se(r > 0); + assert_se(v == 8); + assert_se(u64 == 777); + assert_se(u == 7); + + r = sd_bus_message_read(m, "y(yt)", &v, &u, &u64); + assert_se(r > 0); + assert_se(v == 9); + assert_se(u == 77); + assert_se(u64 == 7777); + + r = sd_bus_message_read(m, "y", &v); + assert_se(r > 0); + assert_se(v == 10); + + r = sd_bus_message_read(m, "()"); + assert_se(r > 0); + + 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_read(m, "a(stdo)", 1, &x, &u64, &dbl, &y); + assert_se(r > 0); + assert_se(streq(x, "foo")); + assert_se(u64 == 815ULL); + assert_se(fabs(dbl - 47.0) < 0.1); + assert_se(streq(y, "/")); + + r = sd_bus_message_peek_type(m, NULL, NULL); + assert_se(r == 0); + + r = sd_bus_message_new_method_call(bus, ©, "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, 0); + 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}y(ty)y(yt)y()"); + 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(); + test_bus_path_encode(); + test_bus_path_encode_unique(); + test_bus_path_encode_many(); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-match.c b/src/libsystemd/src/sd-bus/test-bus-match.c new file mode 100644 index 0000000000..29c4529f95 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-match.c @@ -0,0 +1,159 @@ +/*** + 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 . +***/ + +#include "bus-match.h" +#include "bus-message.h" +#include "bus-slot.h" +#include "bus-util.h" +#include "log.h" +#include "macro.h" + +static bool mask[32]; + +static int filter(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + log_info("Ran %u", PTR_TO_UINT(userdata)); + assert_se(PTR_TO_UINT(userdata) < ELEMENTSOF(mask)); + mask[PTR_TO_UINT(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(sd_bus_slot *slots, struct bus_match_node *root, const char *match, int value) { + struct bus_match_component *components = NULL; + unsigned n_components = 0; + sd_bus_slot *s; + int r; + + s = slots + value; + zero(*s); + + r = bus_match_parse(match, &components, &n_components); + if (r < 0) + return r; + + s->userdata = INT_TO_PTR(value); + s->match_callback.callback = filter; + + r = bus_match_add(root, components, n_components, &s->match_callback); + bus_match_parse_free(components, n_components); + + return r; +} + +static void test_match_scope(const char *match, enum bus_match_scope scope) { + struct bus_match_component *components = NULL; + unsigned n_components = 0; + + assert_se(bus_match_parse(match, &components, &n_components) >= 0); + assert_se(bus_match_get_scope(components, n_components) == scope); + bus_match_parse_free(components, n_components); +} + +int main(int argc, char *argv[]) { + struct bus_match_node root = { + .type = BUS_MATCH_ROOT, + }; + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + enum bus_match_node_type i; + sd_bus_slot slots[19]; + int r; + + r = sd_bus_open_system(&bus); + if (r < 0) + return EXIT_TEST_SKIP; + + assert_se(match_add(slots, &root, "arg2='wal\\'do',sender='foo',type='signal',interface='bar.x',", 1) >= 0); + assert_se(match_add(slots, &root, "arg2='wal\\'do2',sender='foo',type='signal',interface='bar.x',", 2) >= 0); + assert_se(match_add(slots, &root, "arg3='test',sender='foo',type='signal',interface='bar.x',", 3) >= 0); + assert_se(match_add(slots, &root, "arg3='test',sender='foo',type='method_call',interface='bar.x',", 4) >= 0); + assert_se(match_add(slots, &root, "", 5) >= 0); + assert_se(match_add(slots, &root, "interface='quux.x'", 6) >= 0); + assert_se(match_add(slots, &root, "interface='bar.x'", 7) >= 0); + assert_se(match_add(slots, &root, "member='waldo',path='/foo/bar'", 8) >= 0); + assert_se(match_add(slots, &root, "path='/foo/bar'", 9) >= 0); + assert_se(match_add(slots, &root, "path_namespace='/foo'", 10) >= 0); + assert_se(match_add(slots, &root, "path_namespace='/foo/quux'", 11) >= 0); + assert_se(match_add(slots, &root, "arg1='two'", 12) >= 0); + assert_se(match_add(slots, &root, "member='waldo',arg2path='/prefix/'", 13) >= 0); + assert_se(match_add(slots, &root, "member=waldo,path='/foo/bar',arg3namespace='prefix'", 14) >= 0); + assert_se(match_add(slots, &root, "arg4has='pi'", 15) >= 0); + assert_se(match_add(slots, &root, "arg4has='pa'", 16) >= 0); + assert_se(match_add(slots, &root, "arg4has='po'", 17) >= 0); + assert_se(match_add(slots, &root, "arg4='pi'", 18) >= 0); + + bus_match_dump(&root, 0); + + assert_se(sd_bus_message_new_signal(bus, &m, "/foo/bar", "bar.x", "waldo") >= 0); + assert_se(sd_bus_message_append(m, "ssssas", "one", "two", "/prefix/three", "prefix.four", 3, "pi", "pa", "po") >= 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, 15, 16, 17 }, 11)); + + assert_se(bus_match_remove(&root, &slots[8].match_callback) >= 0); + assert_se(bus_match_remove(&root, &slots[13].match_callback) >= 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, 15, 16, 17 }, 9)); + + 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); + + test_match_scope("interface='foobar'", BUS_MATCH_GENERIC); + test_match_scope("", BUS_MATCH_GENERIC); + test_match_scope("interface='org.freedesktop.DBus.Local'", BUS_MATCH_LOCAL); + test_match_scope("sender='org.freedesktop.DBus.Local'", BUS_MATCH_LOCAL); + test_match_scope("member='gurke',path='/org/freedesktop/DBus/Local'", BUS_MATCH_LOCAL); + test_match_scope("arg2='piep',sender='org.freedesktop.DBus',member='waldo'", BUS_MATCH_DRIVER); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-objects.c b/src/libsystemd/src/sd-bus/test-bus-objects.c new file mode 100644 index 0000000000..e9bb655665 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-objects.c @@ -0,0 +1,555 @@ +/*** + 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 . +***/ + +#include +#include + +#include + +#include "alloc-util.h" +#include "bus-dump.h" +#include "bus-internal.h" +#include "bus-message.h" +#include "bus-util.h" +#include "log.h" +#include "macro.h" +#include "strv.h" +#include "util.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_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_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_message *m, void *userdata, sd_bus_error *error) { + int r; + + assert_se(sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), 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_message *m, void *userdata, sd_bus_error *error) { + int r; + + assert_se(sd_bus_emit_properties_changed_strv(sd_bus_message_get_bus(m), 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_message *m, void *userdata, sd_bus_error *error) { + int r; + + assert_se(sd_bus_emit_interfaces_added(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0); + + r = sd_bus_reply_method_return(m, NULL); + assert_se(r >= 0); + + return 1; +} + +static int emit_interfaces_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + int r; + + assert_se(sd_bus_emit_interfaces_removed(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0); + + r = sd_bus_reply_method_return(m, NULL); + assert_se(r >= 0); + + return 1; +} + +static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *error) { + int r; + + assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), "/value/a/x") >= 0); + + r = sd_bus_reply_method_return(m, NULL); + assert_se(r >= 0); + + return 1; +} + +static int emit_object_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + int r; + + assert_se(sd_bus_emit_object_removed(sd_bus_message_get_bus(m), "/value/a/x") >= 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_METHOD("EmitObjectAdded", NULL, NULL, emit_object_added, 0), + SD_BUS_METHOD("EmitObjectRemoved", NULL, NULL, emit_object_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_PROPERTY("AnExplicitProperty", "s", NULL, offsetof(struct context, something), SD_BUS_VTABLE_PROPERTY_EXPLICIT|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_VTABLE_END +}; + +static int enumerator_callback(sd_bus *bus, 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 int enumerator2_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + + if (object_path_startswith("/value/a", path)) + assert_se(*nodes = strv_new("/value/a/x", "/value/a/y", "/value/a/z", 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, NULL, "/foo", "org.freedesktop.systemd.test", vtable, c) >= 0); + assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0); + assert_se(sd_bus_add_fallback_vtable(bus, NULL, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0); + assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value", enumerator_callback, NULL) >= 0); + assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value/a", enumerator2_callback, NULL) >= 0); + assert_se(sd_bus_add_object_manager(bus, NULL, "/value") >= 0); + assert_se(sd_bus_add_object_manager(bus, NULL, "/value/a") >= 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_errno(r, "Failed to process requests: %m"); + goto fail; + } + + if (r == 0) { + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) { + log_error_errno(r, "Failed to wait: %m"); + 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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_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, "<<>>")); + + 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, "<<>>")); + + 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, BUS_MESSAGE_DUMP_WITH_HEADER); + + 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, BUS_MESSAGE_DUMP_WITH_HEADER); + + 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, BUS_MESSAGE_DUMP_WITH_HEADER); + + 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, BUS_MESSAGE_DUMP_WITH_HEADER); + + 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, BUS_MESSAGE_DUMP_WITH_HEADER); + + 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, BUS_MESSAGE_DUMP_WITH_HEADER); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectAdded", &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, BUS_MESSAGE_DUMP_WITH_HEADER); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectRemoved", &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, BUS_MESSAGE_DUMP_WITH_HEADER); + + 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/src/sd-bus/test-bus-server.c b/src/libsystemd/src/sd-bus/test-bus-server.c new file mode 100644 index 0000000000..190410674b --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-server.c @@ -0,0 +1,216 @@ +/*** + 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 . +***/ + +#include +#include + +#include + +#include "bus-internal.h" +#include "bus-util.h" +#include "log.h" +#include "macro.h" +#include "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_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + + r = sd_bus_process(bus, &m); + if (r < 0) { + log_error_errno(r, "Failed to process requests: %m"); + goto fail; + } + + if (r == 0) { + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) { + log_error_errno(r, "Failed to wait: %m"); + 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_errno(r, "Failed to allocate return: %m"); + goto fail; + } + + quit = true; + + } else if (sd_bus_message_is_method_call(m, NULL, NULL)) { + r = sd_bus_message_new_method_error( + m, + &reply, + &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method.")); + if (r < 0) { + log_error_errno(r, "Failed to allocate return: %m"); + goto fail; + } + } + + if (reply) { + r = sd_bus_send(bus, reply, NULL); + if (r < 0) { + log_error_errno(r, "Failed to send reply: %m"); + 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_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + _cleanup_(sd_bus_unrefp) 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, + &m, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.systemd.test", + "Exit"); + if (r < 0) + return log_error_errno(r, "Failed to allocate method call: %m"); + + 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/src/sd-bus/test-bus-signature.c b/src/libsystemd/src/sd-bus/test-bus-signature.c new file mode 100644 index 0000000000..4f4fd093bf --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-signature.c @@ -0,0 +1,164 @@ +/*** + 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 . +***/ + +#include "bus-internal.h" +#include "bus-signature.h" +#include "log.h" +#include "string-util.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(path_complex_pattern("/foobar/waldo", "/foobar/")); + + assert_se(path_simple_pattern("/foo/", "/foo/bar/waldo")); + + assert_se(namespace_simple_pattern("", "")); + assert_se(namespace_simple_pattern("", ".foobar")); + 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(namespace_simple_pattern("foo.", "foo.bar.waldo")); + + 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/src/sd-bus/test-bus-zero-copy.c b/src/libsystemd/src/sd-bus/test-bus-zero-copy.c new file mode 100644 index 0000000000..9e20d67670 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-zero-copy.c @@ -0,0 +1,210 @@ +/*** + 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 . +***/ + +#include + +#include + +#include "alloc-util.h" +#include "bus-dump.h" +#include "bus-kernel.h" +#include "bus-message.h" +#include "fd-util.h" +#include "log.h" +#include "memfd-util.h" +#include "string-util.h" +#include "util.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; + const char *unique; + uint8_t *p; + sd_bus *a, *b; + int r, bus_ref; + sd_bus_message *m; + int f; + uint64_t sz; + uint32_t u32; + size_t i, l; + char *s; + _cleanup_close_ int sfd = -1; + + 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_get_unique_name(a, &unique); + assert_se(r >= 0); + + r = sd_bus_message_new_method_call(b, &m, unique, "/a/path", "an.inter.face", "AMethod"); + 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] = '>'; + + f = memfd_new_and_map(NULL, STRING_SIZE, (void**) &s); + assert_se(f >= 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 = memfd_get_size(f, &sz); + assert_se(r >= 0); + assert_se(sz == STRING_SIZE); + + r = sd_bus_message_append_string_memfd(m, f, 0, (uint64_t) -1); + assert_se(r >= 0); + + close(f); + + f = memfd_new_and_map(NULL, SECOND_ARRAY, (void**) &p); + assert_se(f >= 0); + + p[0] = '<'; + memset(p+1, 'P', SECOND_ARRAY-2); + p[SECOND_ARRAY-1] = '>'; + munmap(p, SECOND_ARRAY); + + r = memfd_get_size(f, &sz); + assert_se(r >= 0); + assert_se(sz == SECOND_ARRAY); + + r = sd_bus_message_append_array_memfd(m, 'y', f, 0, (uint64_t) -1); + assert_se(r >= 0); + + close(f); + + r = sd_bus_message_close_container(m); + assert_se(r >= 0); + + r = sd_bus_message_append(m, "u", 4711); + assert_se(r >= 0); + + assert_se((sfd = memfd_new_and_map(NULL, 6, (void**) &p)) >= 0); + memcpy(p, "abcd\0", 6); + munmap(p, 6); + assert_se(sd_bus_message_append_string_memfd(m, sfd, 1, 4) >= 0); + + r = bus_message_seal(m, 55, 99*USEC_PER_SEC); + assert_se(r >= 0); + + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + 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, BUS_MESSAGE_DUMP_WITH_HEADER); + 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); + + r = sd_bus_message_read(m, "s", &s); + assert_se(r > 0); + assert_se(streq_ptr(s, "bcd")); + + sd_bus_message_unref(m); + + sd_bus_unref(a); + sd_bus_unref(b); + + return 0; +} diff --git a/src/libsystemd/src/sd-daemon/sd-daemon.c b/src/libsystemd/src/sd-daemon/sd-daemon.c new file mode 100644 index 0000000000..fa92199c43 --- /dev/null +++ b/src/libsystemd/src/sd-daemon/sd-daemon.c @@ -0,0 +1,622 @@ +/*** + This file is part of systemd. + + Copyright 2010 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "fd-util.h" +#include "fs-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "socket-util.h" +#include "strv.h" +#include "util.h" + +#define SNDBUF_SIZE (8*1024*1024) + +static void unsetenv_all(bool unset_environment) { + + if (!unset_environment) + return; + + unsetenv("LISTEN_PID"); + unsetenv("LISTEN_FDS"); + unsetenv("LISTEN_FDNAMES"); +} + +_public_ int sd_listen_fds(int unset_environment) { + const char *e; + int n, r, fd; + pid_t pid; + + e = getenv("LISTEN_PID"); + if (!e) { + r = 0; + goto finish; + } + + r = parse_pid(e, &pid); + if (r < 0) + goto finish; + + /* Is this for us? */ + if (getpid() != pid) { + r = 0; + goto finish; + } + + e = getenv("LISTEN_FDS"); + if (!e) { + r = 0; + goto finish; + } + + r = safe_atoi(e, &n); + if (r < 0) + goto finish; + + assert_cc(SD_LISTEN_FDS_START < INT_MAX); + if (n <= 0 || n > INT_MAX - SD_LISTEN_FDS_START) { + r = -EINVAL; + goto finish; + } + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) { + r = fd_cloexec(fd, true); + if (r < 0) + goto finish; + } + + r = n; + +finish: + unsetenv_all(unset_environment); + return r; +} + +_public_ int sd_listen_fds_with_names(int unset_environment, char ***names) { + _cleanup_strv_free_ char **l = NULL; + bool have_names; + int n_names = 0, n_fds; + const char *e; + int r; + + if (!names) + return sd_listen_fds(unset_environment); + + e = getenv("LISTEN_FDNAMES"); + if (e) { + n_names = strv_split_extract(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (n_names < 0) { + unsetenv_all(unset_environment); + return n_names; + } + + have_names = true; + } else + have_names = false; + + n_fds = sd_listen_fds(unset_environment); + if (n_fds <= 0) + return n_fds; + + if (have_names) { + if (n_names != n_fds) + return -EINVAL; + } else { + r = strv_extend_n(&l, "unknown", n_fds); + if (r < 0) + return r; + } + + *names = l; + l = NULL; + + return n_fds; +} + +_public_ int sd_is_fifo(int fd, const char *path) { + struct stat st_fd; + + assert_return(fd >= 0, -EBADF); + + if (fstat(fd, &st_fd) < 0) + return -errno; + + if (!S_ISFIFO(st_fd.st_mode)) + return 0; + + if (path) { + struct stat st_path; + + if (stat(path, &st_path) < 0) { + + if (errno == ENOENT || errno == ENOTDIR) + return 0; + + return -errno; + } + + return + st_path.st_dev == st_fd.st_dev && + st_path.st_ino == st_fd.st_ino; + } + + return 1; +} + +_public_ int sd_is_special(int fd, const char *path) { + struct stat st_fd; + + assert_return(fd >= 0, -EBADF); + + if (fstat(fd, &st_fd) < 0) + return -errno; + + if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode)) + return 0; + + if (path) { + struct stat st_path; + + if (stat(path, &st_path) < 0) { + + if (errno == ENOENT || errno == ENOTDIR) + return 0; + + return -errno; + } + + if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode)) + return + st_path.st_dev == st_fd.st_dev && + st_path.st_ino == st_fd.st_ino; + else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode)) + return st_path.st_rdev == st_fd.st_rdev; + else + return 0; + } + + return 1; +} + +static int sd_is_socket_internal(int fd, int type, int listening) { + struct stat st_fd; + + assert_return(fd >= 0, -EBADF); + assert_return(type >= 0, -EINVAL); + + if (fstat(fd, &st_fd) < 0) + return -errno; + + if (!S_ISSOCK(st_fd.st_mode)) + return 0; + + if (type != 0) { + int other_type = 0; + socklen_t l = sizeof(other_type); + + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0) + return -errno; + + if (l != sizeof(other_type)) + return -EINVAL; + + if (other_type != type) + return 0; + } + + if (listening >= 0) { + int accepting = 0; + socklen_t l = sizeof(accepting); + + if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) + return -errno; + + if (l != sizeof(accepting)) + return -EINVAL; + + if (!accepting != !listening) + return 0; + } + + return 1; +} + +_public_ int sd_is_socket(int fd, int family, int type, int listening) { + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(family >= 0, -EINVAL); + + r = sd_is_socket_internal(fd, type, listening); + if (r <= 0) + return r; + + if (family > 0) { + union sockaddr_union sockaddr = {}; + socklen_t l = sizeof(sockaddr); + + if (getsockname(fd, &sockaddr.sa, &l) < 0) + return -errno; + + if (l < sizeof(sa_family_t)) + return -EINVAL; + + return sockaddr.sa.sa_family == family; + } + + return 1; +} + +_public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { + union sockaddr_union sockaddr = {}; + socklen_t l = sizeof(sockaddr); + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL); + + r = sd_is_socket_internal(fd, type, listening); + if (r <= 0) + return r; + + if (getsockname(fd, &sockaddr.sa, &l) < 0) + return -errno; + + if (l < sizeof(sa_family_t)) + return -EINVAL; + + if (sockaddr.sa.sa_family != AF_INET && + sockaddr.sa.sa_family != AF_INET6) + return 0; + + if (family != 0) + if (sockaddr.sa.sa_family != family) + return 0; + + if (port > 0) { + if (sockaddr.sa.sa_family == AF_INET) { + if (l < sizeof(struct sockaddr_in)) + return -EINVAL; + + return htons(port) == sockaddr.in.sin_port; + } else { + if (l < sizeof(struct sockaddr_in6)) + return -EINVAL; + + return htons(port) == sockaddr.in6.sin6_port; + } + } + + return 1; +} + +_public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { + union sockaddr_union sockaddr = {}; + socklen_t l = sizeof(sockaddr); + int r; + + assert_return(fd >= 0, -EBADF); + + r = sd_is_socket_internal(fd, type, listening); + if (r <= 0) + return r; + + if (getsockname(fd, &sockaddr.sa, &l) < 0) + return -errno; + + if (l < sizeof(sa_family_t)) + return -EINVAL; + + if (sockaddr.sa.sa_family != AF_UNIX) + return 0; + + if (path) { + if (length == 0) + length = strlen(path); + + if (length == 0) + /* Unnamed socket */ + return l == offsetof(struct sockaddr_un, sun_path); + + if (path[0]) + /* Normal path socket */ + return + (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) && + memcmp(path, sockaddr.un.sun_path, length+1) == 0; + else + /* Abstract namespace socket */ + return + (l == offsetof(struct sockaddr_un, sun_path) + length) && + memcmp(path, sockaddr.un.sun_path, length) == 0; + } + + return 1; +} + +_public_ int sd_is_mq(int fd, const char *path) { + struct mq_attr attr; + + /* Check that the fd is valid */ + assert_return(fcntl(fd, F_GETFD) >= 0, -errno); + + if (mq_getattr(fd, &attr) < 0) { + if (errno == EBADF) + /* A non-mq fd (or an invalid one, but we ruled that out above) */ + return 0; + return -errno; + } + + if (path) { + char fpath[PATH_MAX]; + struct stat a, b; + + assert_return(path_is_absolute(path), -EINVAL); + + if (fstat(fd, &a) < 0) + return -errno; + + strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12); + fpath[sizeof(fpath)-1] = 0; + + if (stat(fpath, &b) < 0) + return -errno; + + if (a.st_dev != b.st_dev || + a.st_ino != b.st_ino) + return 0; + } + + return 1; +} + +_public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) { + union sockaddr_union sockaddr = { + .sa.sa_family = AF_UNIX, + }; + struct iovec iovec = { + .iov_base = (char*) state, + }; + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_name = &sockaddr, + }; + _cleanup_close_ int fd = -1; + struct cmsghdr *cmsg = NULL; + const char *e; + bool have_pid; + int r; + + if (!state) { + r = -EINVAL; + goto finish; + } + + if (n_fds > 0 && !fds) { + r = -EINVAL; + goto finish; + } + + e = getenv("NOTIFY_SOCKET"); + if (!e) + return 0; + + /* Must be an abstract socket, or an absolute path */ + if ((e[0] != '@' && e[0] != '/') || e[1] == 0) { + r = -EINVAL; + goto finish; + } + + if (strlen(e) > sizeof(sockaddr.un.sun_path)) { + r = -EINVAL; + goto finish; + } + + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (fd < 0) { + r = -errno; + goto finish; + } + + fd_inc_sndbuf(fd, SNDBUF_SIZE); + + iovec.iov_len = strlen(state); + + strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path)); + if (sockaddr.un.sun_path[0] == '@') + sockaddr.un.sun_path[0] = 0; + + msghdr.msg_namelen = SOCKADDR_UN_LEN(sockaddr.un); + + have_pid = pid != 0 && pid != getpid(); + + if (n_fds > 0 || have_pid) { + /* CMSG_SPACE(0) may return value different than zero, which results in miscalculated controllen. */ + msghdr.msg_controllen = + (n_fds > 0 ? CMSG_SPACE(sizeof(int) * n_fds) : 0) + + (have_pid ? CMSG_SPACE(sizeof(struct ucred)) : 0); + + msghdr.msg_control = alloca0(msghdr.msg_controllen); + + cmsg = CMSG_FIRSTHDR(&msghdr); + if (n_fds > 0) { + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds); + + memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds); + + if (have_pid) + assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg)); + } + + if (have_pid) { + struct ucred *ucred; + + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + + ucred = (struct ucred*) CMSG_DATA(cmsg); + ucred->pid = pid; + ucred->uid = getuid(); + ucred->gid = getgid(); + } + } + + /* First try with fake ucred data, as requested */ + if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) { + r = 1; + goto finish; + } + + /* If that failed, try with our own ucred instead */ + if (have_pid) { + msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred)); + if (msghdr.msg_controllen == 0) + msghdr.msg_control = NULL; + + if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) { + r = 1; + goto finish; + } + } + + r = -errno; + +finish: + if (unset_environment) + unsetenv("NOTIFY_SOCKET"); + + return r; +} + +_public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) { + return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0); +} + +_public_ int sd_notify(int unset_environment, const char *state) { + return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0); +} + +_public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) { + _cleanup_free_ char *p = NULL; + int r; + + if (format) { + va_list ap; + + va_start(ap, format); + r = vasprintf(&p, format, ap); + va_end(ap); + + if (r < 0 || !p) + return -ENOMEM; + } + + return sd_pid_notify(pid, unset_environment, p); +} + +_public_ int sd_notifyf(int unset_environment, const char *format, ...) { + _cleanup_free_ char *p = NULL; + int r; + + if (format) { + va_list ap; + + va_start(ap, format); + r = vasprintf(&p, format, ap); + va_end(ap); + + if (r < 0 || !p) + return -ENOMEM; + } + + return sd_pid_notify(0, unset_environment, p); +} + +_public_ int sd_booted(void) { + /* We test whether the runtime unit file directory has been + * created. This takes place in mount-setup.c, so is + * guaranteed to happen very early during boot. */ + + return laccess("/run/systemd/system/", F_OK) >= 0; +} + +_public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) { + const char *s, *p = ""; /* p is set to dummy value to do unsetting */ + uint64_t u; + int r = 0; + + s = getenv("WATCHDOG_USEC"); + if (!s) + goto finish; + + r = safe_atou64(s, &u); + if (r < 0) + goto finish; + if (u <= 0 || u >= USEC_INFINITY) { + r = -EINVAL; + goto finish; + } + + p = getenv("WATCHDOG_PID"); + if (p) { + pid_t pid; + + r = parse_pid(p, &pid); + if (r < 0) + goto finish; + + /* Is this for us? */ + if (getpid() != pid) { + r = 0; + goto finish; + } + } + + if (usec) + *usec = u; + + r = 1; + +finish: + if (unset_environment && s) + unsetenv("WATCHDOG_USEC"); + if (unset_environment && p) + unsetenv("WATCHDOG_PID"); + + return r; +} diff --git a/src/libsystemd/src/sd-device/device-enumerator-private.h b/src/libsystemd/src/sd-device/device-enumerator-private.h new file mode 100644 index 0000000000..d46e26b56e --- /dev/null +++ b/src/libsystemd/src/sd-device/device-enumerator-private.h @@ -0,0 +1,34 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Tom Gundersen + + 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 . +***/ + +#include + +int device_enumerator_scan_devices(sd_device_enumerator *enumeartor); +int device_enumerator_scan_subsystems(sd_device_enumerator *enumeartor); +int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device); +int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator); +sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator); +sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator); + +#define FOREACH_DEVICE_AND_SUBSYSTEM(enumerator, device) \ + for (device = device_enumerator_get_first(enumerator); \ + device; \ + device = device_enumerator_get_next(enumerator)) diff --git a/src/libsystemd/src/sd-device/device-enumerator.c b/src/libsystemd/src/sd-device/device-enumerator.c new file mode 100644 index 0000000000..796728ee0e --- /dev/null +++ b/src/libsystemd/src/sd-device/device-enumerator.c @@ -0,0 +1,986 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + Copyright 2014-2015 Tom Gundersen + + 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 . +***/ + +#include + +#include "alloc-util.h" +#include "device-enumerator-private.h" +#include "device-util.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "prioq.h" +#include "set.h" +#include "string-util.h" +#include "strv.h" +#include "util.h" + +#define DEVICE_ENUMERATE_MAX_DEPTH 256 + +typedef enum DeviceEnumerationType { + DEVICE_ENUMERATION_TYPE_DEVICES, + DEVICE_ENUMERATION_TYPE_SUBSYSTEMS, + _DEVICE_ENUMERATION_TYPE_MAX, + _DEVICE_ENUMERATION_TYPE_INVALID = -1, +} DeviceEnumerationType; + +struct sd_device_enumerator { + unsigned n_ref; + + DeviceEnumerationType type; + Prioq *devices; + bool scan_uptodate; + + Set *match_subsystem; + Set *nomatch_subsystem; + Hashmap *match_sysattr; + Hashmap *nomatch_sysattr; + Hashmap *match_property; + Set *match_sysname; + Set *match_tag; + sd_device *match_parent; + bool match_allow_uninitialized; +}; + +_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerator = NULL; + + assert(ret); + + enumerator = new0(sd_device_enumerator, 1); + if (!enumerator) + return -ENOMEM; + + enumerator->n_ref = 1; + enumerator->type = _DEVICE_ENUMERATION_TYPE_INVALID; + + *ret = enumerator; + enumerator = NULL; + + return 0; +} + +_public_ sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + assert_se((++ enumerator->n_ref) >= 2); + + return enumerator; +} + +_public_ sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator) { + if (enumerator && (-- enumerator->n_ref) == 0) { + sd_device *device; + + while ((device = prioq_pop(enumerator->devices))) + sd_device_unref(device); + + prioq_free(enumerator->devices); + + set_free_free(enumerator->match_subsystem); + set_free_free(enumerator->nomatch_subsystem); + hashmap_free_free_free(enumerator->match_sysattr); + hashmap_free_free_free(enumerator->nomatch_sysattr); + hashmap_free_free_free(enumerator->match_property); + set_free_free(enumerator->match_sysname); + set_free_free(enumerator->match_tag); + sd_device_unref(enumerator->match_parent); + + free(enumerator); + } + + return NULL; +} + +_public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) { + Set **set; + int r; + + assert_return(enumerator, -EINVAL); + assert_return(subsystem, -EINVAL); + + if (match) + set = &enumerator->match_subsystem; + else + set = &enumerator->nomatch_subsystem; + + r = set_ensure_allocated(set, NULL); + if (r < 0) + return r; + + r = set_put_strdup(*set, subsystem); + if (r < 0) + return r; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *_sysattr, const char *_value, int match) { + _cleanup_free_ char *sysattr = NULL, *value = NULL; + Hashmap **hashmap; + int r; + + assert_return(enumerator, -EINVAL); + assert_return(_sysattr, -EINVAL); + + if (match) + hashmap = &enumerator->match_sysattr; + else + hashmap = &enumerator->nomatch_sysattr; + + r = hashmap_ensure_allocated(hashmap, NULL); + if (r < 0) + return r; + + sysattr = strdup(_sysattr); + if (!sysattr) + return -ENOMEM; + + if (_value) { + value = strdup(_value); + if (!value) + return -ENOMEM; + } + + r = hashmap_put(*hashmap, sysattr, value); + if (r < 0) + return r; + + sysattr = NULL; + value = NULL; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *_property, const char *_value) { + _cleanup_free_ char *property = NULL, *value = NULL; + int r; + + assert_return(enumerator, -EINVAL); + assert_return(_property, -EINVAL); + + r = hashmap_ensure_allocated(&enumerator->match_property, NULL); + if (r < 0) + return r; + + property = strdup(_property); + if (!property) + return -ENOMEM; + + if (_value) { + value = strdup(_value); + if (!value) + return -ENOMEM; + } + + r = hashmap_put(enumerator->match_property, property, value); + if (r < 0) + return r; + + property = NULL; + value = NULL; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname) { + int r; + + assert_return(enumerator, -EINVAL); + assert_return(sysname, -EINVAL); + + r = set_ensure_allocated(&enumerator->match_sysname, NULL); + if (r < 0) + return r; + + r = set_put_strdup(enumerator->match_sysname, sysname); + if (r < 0) + return r; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag) { + int r; + + assert_return(enumerator, -EINVAL); + assert_return(tag, -EINVAL); + + r = set_ensure_allocated(&enumerator->match_tag, NULL); + if (r < 0) + return r; + + r = set_put_strdup(enumerator->match_tag, tag); + if (r < 0) + return r; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) { + assert_return(enumerator, -EINVAL); + assert_return(parent, -EINVAL); + + sd_device_unref(enumerator->match_parent); + enumerator->match_parent = sd_device_ref(parent); + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) { + assert_return(enumerator, -EINVAL); + + enumerator->match_allow_uninitialized = true; + + enumerator->scan_uptodate = false; + + return 0; +} + +int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) { + assert_return(enumerator, -EINVAL); + + enumerator->match_allow_uninitialized = false; + + enumerator->scan_uptodate = false; + + return 0; +} + +static int device_compare(const void *_a, const void *_b) { + sd_device *a = (sd_device *)_a, *b = (sd_device *)_b; + const char *devpath_a, *devpath_b, *sound_a; + bool delay_a, delay_b; + + assert_se(sd_device_get_devpath(a, &devpath_a) >= 0); + assert_se(sd_device_get_devpath(b, &devpath_b) >= 0); + + sound_a = strstr(devpath_a, "/sound/card"); + if (sound_a) { + /* For sound cards the control device must be enumerated last to + * make sure it's the final device node that gets ACLs applied. + * Applications rely on this fact and use ACL changes on the + * control node as an indicator that the ACL change of the + * entire sound card completed. The kernel makes this guarantee + * when creating those devices, and hence we should too when + * enumerating them. */ + sound_a += strlen("/sound/card"); + sound_a = strchr(sound_a, '/'); + + if (sound_a) { + unsigned prefix_len; + + prefix_len = sound_a - devpath_a; + + if (strncmp(devpath_a, devpath_b, prefix_len) == 0) { + const char *sound_b; + + sound_b = devpath_b + prefix_len; + + if (startswith(sound_a, "/controlC") && + !startswith(sound_b, "/contolC")) + return 1; + + if (!startswith(sound_a, "/controlC") && + startswith(sound_b, "/controlC")) + return -1; + } + } + } + + /* md and dm devices are enumerated after all other devices */ + delay_a = strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-"); + delay_b = strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-"); + if (delay_a != delay_b) + return delay_a - delay_b; + + return strcmp(devpath_a, devpath_b); +} + +int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) { + int r; + + assert_return(enumerator, -EINVAL); + assert_return(device, -EINVAL); + + r = prioq_ensure_allocated(&enumerator->devices, device_compare); + if (r < 0) + return r; + + r = prioq_put(enumerator->devices, device, NULL); + if (r < 0) + return r; + + sd_device_ref(device); + + return 0; +} + +static bool match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) { + const char *value; + int r; + + assert(device); + assert(sysattr); + + r = sd_device_get_sysattr_value(device, sysattr, &value); + if (r < 0) + return false; + + if (!match_value) + return true; + + if (fnmatch(match_value, value, 0) == 0) + return true; + + return false; +} + +static bool match_sysattr(sd_device_enumerator *enumerator, sd_device *device) { + const char *sysattr; + const char *value; + Iterator i; + + assert(enumerator); + assert(device); + + HASHMAP_FOREACH_KEY(value, sysattr, enumerator->nomatch_sysattr, i) + if (match_sysattr_value(device, sysattr, value)) + return false; + + HASHMAP_FOREACH_KEY(value, sysattr, enumerator->match_sysattr, i) + if (!match_sysattr_value(device, sysattr, value)) + return false; + + return true; +} + +static bool match_property(sd_device_enumerator *enumerator, sd_device *device) { + const char *property; + const char *value; + Iterator i; + + assert(enumerator); + assert(device); + + if (hashmap_isempty(enumerator->match_property)) + return true; + + HASHMAP_FOREACH_KEY(value, property, enumerator->match_property, i) { + const char *property_dev, *value_dev; + + FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) { + if (fnmatch(property, property_dev, 0) != 0) + continue; + + if (!value && !value_dev) + return true; + + if (!value || !value_dev) + continue; + + if (fnmatch(value, value_dev, 0) == 0) + return true; + } + } + + return false; +} + +static bool match_tag(sd_device_enumerator *enumerator, sd_device *device) { + const char *tag; + Iterator i; + + assert(enumerator); + assert(device); + + SET_FOREACH(tag, enumerator->match_tag, i) + if (!sd_device_has_tag(device, tag)) + return false; + + return true; +} + +static bool match_parent(sd_device_enumerator *enumerator, sd_device *device) { + const char *devpath, *devpath_dev; + int r; + + assert(enumerator); + assert(device); + + if (!enumerator->match_parent) + return true; + + r = sd_device_get_devpath(enumerator->match_parent, &devpath); + assert(r >= 0); + + r = sd_device_get_devpath(device, &devpath_dev); + assert(r >= 0); + + return startswith(devpath_dev, devpath); +} + +static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) { + const char *sysname_match; + Iterator i; + + assert(enumerator); + assert(sysname); + + if (set_isempty(enumerator->match_sysname)) + return true; + + SET_FOREACH(sysname_match, enumerator->match_sysname, i) + if (fnmatch(sysname_match, sysname, 0) == 0) + return true; + + return false; +} + +static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) { + _cleanup_closedir_ DIR *dir = NULL; + char *path; + struct dirent *dent; + int r = 0; + + assert(enumerator); + assert(basedir); + + path = strjoina("/sys/", basedir, "/"); + + if (subdir1) + path = strjoina(path, subdir1, "/"); + + if (subdir2) + path = strjoina(path, subdir2, "/"); + + dir = opendir(path); + if (!dir) + return -errno; + + FOREACH_DIRENT_ALL(dent, dir, return -errno) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + char syspath[strlen(path) + 1 + strlen(dent->d_name) + 1]; + dev_t devnum; + int ifindex, initialized, k; + + if (dent->d_name[0] == '.') + continue; + + if (!match_sysname(enumerator, dent->d_name)) + continue; + + (void)sprintf(syspath, "%s%s", path, dent->d_name); + + k = sd_device_new_from_syspath(&device, syspath); + if (k < 0) { + if (k != -ENODEV) + /* this is necessarily racey, so ignore missing devices */ + r = k; + + continue; + } + + k = sd_device_get_devnum(device, &devnum); + if (k < 0) { + r = k; + continue; + } + + k = sd_device_get_ifindex(device, &ifindex); + if (k < 0) { + r = k; + continue; + } + + k = sd_device_get_is_initialized(device, &initialized); + if (k < 0) { + r = k; + continue; + } + + /* + * All devices with a device node or network interfaces + * possibly need udev to adjust the device node permission + * or context, or rename the interface before it can be + * reliably used from other processes. + * + * For now, we can only check these types of devices, we + * might not store a database, and have no way to find out + * for all other types of devices. + */ + if (!enumerator->match_allow_uninitialized && + !initialized && + (major(devnum) > 0 || ifindex > 0)) + continue; + + if (!match_parent(enumerator, device)) + continue; + + if (!match_tag(enumerator, device)) + continue; + + if (!match_property(enumerator, device)) + continue; + + if (!match_sysattr(enumerator, device)) + continue; + + k = device_enumerator_add_device(enumerator, device); + if (k < 0) + r = k; + } + + return r; +} + +static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsystem) { + const char *subsystem_match; + Iterator i; + + assert(enumerator); + + if (!subsystem) + return false; + + SET_FOREACH(subsystem_match, enumerator->nomatch_subsystem, i) + if (fnmatch(subsystem_match, subsystem, 0) == 0) + return false; + + if (set_isempty(enumerator->match_subsystem)) + return true; + + SET_FOREACH(subsystem_match, enumerator->match_subsystem, i) + if (fnmatch(subsystem_match, subsystem, 0) == 0) + return true; + + return false; +} + +static int enumerator_scan_dir(sd_device_enumerator *enumerator, const char *basedir, const char *subdir, const char *subsystem) { + _cleanup_closedir_ DIR *dir = NULL; + char *path; + struct dirent *dent; + int r = 0; + + path = strjoina("/sys/", basedir); + + dir = opendir(path); + if (!dir) + return -errno; + + log_debug(" device-enumerator: scanning %s", path); + + FOREACH_DIRENT_ALL(dent, dir, return -errno) { + int k; + + if (dent->d_name[0] == '.') + continue; + + if (!match_subsystem(enumerator, subsystem ? : dent->d_name)) + continue; + + k = enumerator_scan_dir_and_add_devices(enumerator, basedir, dent->d_name, subdir); + if (k < 0) + r = k; + } + + return r; +} + +static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const char *tag) { + _cleanup_closedir_ DIR *dir = NULL; + char *path; + struct dirent *dent; + int r = 0; + + assert(enumerator); + assert(tag); + + path = strjoina("/run/udev/tags/", tag); + + dir = opendir(path); + if (!dir) { + if (errno == ENOENT) + return 0; + else { + log_error("sd-device-enumerator: could not open tags directory %s: %m", path); + return -errno; + } + } + + /* TODO: filter away subsystems? */ + + FOREACH_DIRENT_ALL(dent, dir, return -errno) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + const char *subsystem, *sysname; + int k; + + if (dent->d_name[0] == '.') + continue; + + k = sd_device_new_from_device_id(&device, dent->d_name); + if (k < 0) { + if (k != -ENODEV) + /* this is necessarily racy, so ignore missing devices */ + r = k; + + continue; + } + + k = sd_device_get_subsystem(device, &subsystem); + if (k < 0) { + r = k; + continue; + } + + if (!match_subsystem(enumerator, subsystem)) + continue; + + k = sd_device_get_sysname(device, &sysname); + if (k < 0) { + r = k; + continue; + } + + if (!match_sysname(enumerator, sysname)) + continue; + + if (!match_parent(enumerator, device)) + continue; + + if (!match_property(enumerator, device)) + continue; + + if (!match_sysattr(enumerator, device)) + continue; + + k = device_enumerator_add_device(enumerator, device); + if (k < 0) { + r = k; + continue; + } + } + + return r; +} + +static int enumerator_scan_devices_tags(sd_device_enumerator *enumerator) { + const char *tag; + Iterator i; + int r; + + assert(enumerator); + + SET_FOREACH(tag, enumerator->match_tag, i) { + r = enumerator_scan_devices_tag(enumerator, tag); + if (r < 0) + return r; + } + + return 0; +} + +static int parent_add_child(sd_device_enumerator *enumerator, const char *path) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + const char *subsystem, *sysname; + int r; + + r = sd_device_new_from_syspath(&device, path); + if (r == -ENODEV) + /* this is necessarily racy, so ignore missing devices */ + return 0; + else if (r < 0) + return r; + + r = sd_device_get_subsystem(device, &subsystem); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + if (!match_subsystem(enumerator, subsystem)) + return 0; + + r = sd_device_get_sysname(device, &sysname); + if (r < 0) + return r; + + if (!match_sysname(enumerator, sysname)) + return 0; + + if (!match_property(enumerator, device)) + return 0; + + if (!match_sysattr(enumerator, device)) + return 0; + + r = device_enumerator_add_device(enumerator, device); + if (r < 0) + return r; + + return 1; +} + +static int parent_crawl_children(sd_device_enumerator *enumerator, const char *path, unsigned maxdepth) { + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *dent; + int r = 0; + + dir = opendir(path); + if (!dir) { + log_debug("sd-device-enumerate: could not open parent directory %s: %m", path); + return -errno; + } + + FOREACH_DIRENT_ALL(dent, dir, return -errno) { + _cleanup_free_ char *child = NULL; + int k; + + if (dent->d_name[0] == '.') + continue; + + if (dent->d_type != DT_DIR) + continue; + + child = strjoin(path, "/", dent->d_name, NULL); + if (!child) + return -ENOMEM; + + k = parent_add_child(enumerator, child); + if (k < 0) + r = k; + + if (maxdepth > 0) + parent_crawl_children(enumerator, child, maxdepth - 1); + else + log_debug("device-enumerate: max depth reached, %s: ignoring devices", child); + } + + return r; +} + +static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) { + const char *path; + int r = 0, k; + + r = sd_device_get_syspath(enumerator->match_parent, &path); + if (r < 0) + return r; + + k = parent_add_child(enumerator, path); + if (k < 0) + r = k; + + k = parent_crawl_children(enumerator, path, DEVICE_ENUMERATE_MAX_DEPTH); + if (k < 0) + r = k; + + return r; +} + +static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) { + int r = 0; + + log_debug("device-enumerator: scan all dirs"); + + if (access("/sys/subsystem", F_OK) >= 0) { + /* we have /subsystem/, forget all the old stuff */ + r = enumerator_scan_dir(enumerator, "subsystem", "devices", NULL); + if (r < 0) + return log_debug_errno(r, "device-enumerator: failed to scan /sys/subsystem: %m"); + } else { + int k; + + k = enumerator_scan_dir(enumerator, "bus", "devices", NULL); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan /sys/bus: %m"); + r = k; + } + + k = enumerator_scan_dir(enumerator, "class", NULL, NULL); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan /sys/class: %m"); + r = k; + } + } + + return r; +} + +int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { + sd_device *device; + int r; + + assert(enumerator); + + if (enumerator->scan_uptodate && + enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES) + return 0; + + while ((device = prioq_pop(enumerator->devices))) + sd_device_unref(device); + + if (!set_isempty(enumerator->match_tag)) { + r = enumerator_scan_devices_tags(enumerator); + if (r < 0) + return r; + } else if (enumerator->match_parent) { + r = enumerator_scan_devices_children(enumerator); + if (r < 0) + return r; + } else { + r = enumerator_scan_devices_all(enumerator); + if (r < 0) + return r; + } + + enumerator->scan_uptodate = true; + + return 0; +} + +_public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) { + int r; + + assert_return(enumerator, NULL); + + r = device_enumerator_scan_devices(enumerator); + if (r < 0) + return NULL; + + enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES; + + return prioq_peek(enumerator->devices); +} + +_public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + if (!enumerator->scan_uptodate || + enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES) + return NULL; + + sd_device_unref(prioq_pop(enumerator->devices)); + + return prioq_peek(enumerator->devices); +} + +int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { + sd_device *device; + const char *subsysdir; + int r = 0, k; + + assert(enumerator); + + if (enumerator->scan_uptodate && + enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS) + return 0; + + while ((device = prioq_pop(enumerator->devices))) + sd_device_unref(device); + + /* modules */ + if (match_subsystem(enumerator, "module")) { + k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan modules: %m"); + r = k; + } + } + + if (access("/sys/subsystem", F_OK) >= 0) + subsysdir = "subsystem"; + else + subsysdir = "bus"; + + /* subsystems (only buses support coldplug) */ + if (match_subsystem(enumerator, "subsystem")) { + k = enumerator_scan_dir_and_add_devices(enumerator, subsysdir, NULL, NULL); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan subsystems: %m"); + r = k; + } + } + + /* subsystem drivers */ + if (match_subsystem(enumerator, "drivers")) { + k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers"); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan drivers: %m"); + r = k; + } + } + + enumerator->scan_uptodate = true; + + return r; +} + +_public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator) { + int r; + + assert_return(enumerator, NULL); + + r = device_enumerator_scan_subsystems(enumerator); + if (r < 0) + return NULL; + + enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS; + + return prioq_peek(enumerator->devices); +} + +_public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + if (enumerator->scan_uptodate || + enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS) + return NULL; + + sd_device_unref(prioq_pop(enumerator->devices)); + + return prioq_peek(enumerator->devices); +} + +sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + return prioq_peek(enumerator->devices); +} + +sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + sd_device_unref(prioq_pop(enumerator->devices)); + + return prioq_peek(enumerator->devices); +} diff --git a/src/libsystemd/src/sd-device/device-internal.h b/src/libsystemd/src/sd-device/device-internal.h new file mode 100644 index 0000000000..ab222e27de --- /dev/null +++ b/src/libsystemd/src/sd-device/device-internal.h @@ -0,0 +1,126 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + Copyright 2014 Tom Gundersen + + 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 . +***/ + +#include "hashmap.h" +#include "set.h" + +struct sd_device { + uint64_t n_ref; + + sd_device *parent; + bool parent_set; /* no need to try to reload parent */ + + OrderedHashmap *properties; + Iterator properties_iterator; + uint64_t properties_generation; /* changes whenever the properties are changed */ + uint64_t properties_iterator_generation; /* generation when iteration was started */ + + /* the subset of the properties that should be written to the db*/ + OrderedHashmap *properties_db; + + Hashmap *sysattr_values; /* cached sysattr values */ + + Set *sysattrs; /* names of sysattrs */ + Iterator sysattrs_iterator; + bool sysattrs_read; /* don't try to re-read sysattrs once read */ + + Set *tags; + Iterator tags_iterator; + uint64_t tags_generation; /* changes whenever the tags are changed */ + uint64_t tags_iterator_generation; /* generation when iteration was started */ + bool property_tags_outdated; /* need to update TAGS= property */ + + Set *devlinks; + Iterator devlinks_iterator; + uint64_t devlinks_generation; /* changes whenever the devlinks are changed */ + uint64_t devlinks_iterator_generation; /* generation when iteration was started */ + bool property_devlinks_outdated; /* need to update DEVLINKS= property */ + int devlink_priority; + + char **properties_strv; /* the properties hashmap as a strv */ + uint8_t *properties_nulstr; /* the same as a nulstr */ + size_t properties_nulstr_len; + bool properties_buf_outdated; /* need to reread hashmap */ + + int watch_handle; + + char *syspath; + const char *devpath; + const char *sysnum; + char *sysname; + bool sysname_set; /* don't reread sysname */ + + char *devtype; + int ifindex; + char *devname; + dev_t devnum; + + char *subsystem; + bool subsystem_set; /* don't reread subsystem */ + char *driver; + bool driver_set; /* don't reread driver */ + + char *id_filename; + + bool is_initialized; + uint64_t usec_initialized; + + mode_t devmode; + uid_t devuid; + gid_t devgid; + + bool uevent_loaded; /* don't reread uevent */ + bool db_loaded; /* don't reread db */ + + bool sealed; /* don't read more information from uevent/db */ + bool db_persist; /* don't clean up the db when switching from initrd to real root */ +}; + +typedef enum DeviceAction { + DEVICE_ACTION_ADD, + DEVICE_ACTION_REMOVE, + DEVICE_ACTION_CHANGE, + DEVICE_ACTION_MOVE, + DEVICE_ACTION_ONLINE, + DEVICE_ACTION_OFFLINE, + _DEVICE_ACTION_MAX, + _DEVICE_ACTION_INVALID = -1, +} DeviceAction; + +int device_new_aux(sd_device **ret); +int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db); +int device_add_property_internal(sd_device *device, const char *key, const char *value); +int device_read_uevent_file(sd_device *device); +int device_read_db_aux(sd_device *device, bool force); + +int device_set_syspath(sd_device *device, const char *_syspath, bool verify); +int device_set_ifindex(sd_device *device, const char *ifindex); +int device_set_devmode(sd_device *device, const char *devmode); +int device_set_devname(sd_device *device, const char *_devname); +int device_set_devtype(sd_device *device, const char *_devtype); +int device_set_devnum(sd_device *device, const char *major, const char *minor); +int device_set_subsystem(sd_device *device, const char *_subsystem); +int device_set_driver(sd_device *device, const char *_driver); +int device_set_usec_initialized(sd_device *device, const char *initialized); + +DeviceAction device_action_from_string(const char *s) _pure_; +const char *device_action_to_string(DeviceAction a) _const_; diff --git a/src/libsystemd/src/sd-device/device-private.c b/src/libsystemd/src/sd-device/device-private.c new file mode 100644 index 0000000000..51cabcb048 --- /dev/null +++ b/src/libsystemd/src/sd-device/device-private.c @@ -0,0 +1,1119 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + Copyright 2014 Tom Gundersen + + 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 . +***/ + +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "device-internal.h" +#include "device-private.h" +#include "device-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "hashmap.h" +#include "macro.h" +#include "mkdir.h" +#include "parse-util.h" +#include "path-util.h" +#include "refcnt.h" +#include "set.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" +#include "strxcpyx.h" +#include "user-util.h" +#include "util.h" + +int device_add_property(sd_device *device, const char *key, const char *value) { + int r; + + assert(device); + assert(key); + + r = device_add_property_aux(device, key, value, false); + if (r < 0) + return r; + + if (key[0] != '.') { + r = device_add_property_aux(device, key, value, true); + if (r < 0) + return r; + } + + return 0; +} + +static int device_add_property_internal_from_string(sd_device *device, const char *str) { + _cleanup_free_ char *key = NULL; + char *value; + + assert(device); + assert(str); + + key = strdup(str); + if (!key) + return -ENOMEM; + + value = strchr(key, '='); + if (!value) + return -EINVAL; + + *value = '\0'; + + if (isempty(++value)) + value = NULL; + + return device_add_property_internal(device, key, value); +} + +static int handle_db_line(sd_device *device, char key, const char *value) { + char *path; + int r; + + assert(device); + assert(value); + + switch (key) { + case 'S': + path = strjoina("/dev/", value); + r = device_add_devlink(device, path); + if (r < 0) + return r; + + break; + case 'L': + r = safe_atoi(value, &device->devlink_priority); + if (r < 0) + return r; + + break; + case 'E': + r = device_add_property_internal_from_string(device, value); + if (r < 0) + return r; + + break; + case 'G': + r = device_add_tag(device, value); + if (r < 0) + return r; + + break; + case 'W': + r = safe_atoi(value, &device->watch_handle); + if (r < 0) + return r; + + break; + case 'I': + r = device_set_usec_initialized(device, value); + if (r < 0) + return r; + + break; + default: + log_debug("device db: unknown key '%c'", key); + } + + return 0; +} + +void device_set_devlink_priority(sd_device *device, int priority) { + assert(device); + + device->devlink_priority = priority; +} + +void device_set_is_initialized(sd_device *device) { + assert(device); + + device->is_initialized = true; +} + +int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) { + char num[DECIMAL_STR_MAX(usec_t)]; + usec_t usec_initialized; + int r; + + assert(device); + + if (device_old && device_old->usec_initialized > 0) + usec_initialized = device_old->usec_initialized; + else + usec_initialized = now(CLOCK_MONOTONIC); + + r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized); + if (r < 0) + return -errno; + + r = device_set_usec_initialized(device, num); + if (r < 0) + return r; + + return 0; +} + +static int device_read_db(sd_device *device) { + _cleanup_free_ char *db = NULL; + char *path; + const char *id, *value; + char key; + size_t db_len; + unsigned i; + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + INVALID_LINE, + } state = PRE_KEY; + + assert(device); + + if (device->db_loaded || device->sealed) + return 0; + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + r = read_full_file(path, &db, &db_len); + if (r < 0) { + if (r == -ENOENT) + return 0; + else + return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path); + } + + /* devices with a database entry are initialized */ + device_set_is_initialized(device); + + for (i = 0; i < db_len; i++) { + switch (state) { + case PRE_KEY: + if (!strchr(NEWLINE, db[i])) { + key = db[i]; + + state = KEY; + } + + break; + case KEY: + if (db[i] != ':') { + log_debug("sd-device: ignoring invalid db entry with key '%c'", key); + + state = INVALID_LINE; + } else { + db[i] = '\0'; + + state = PRE_VALUE; + } + + break; + case PRE_VALUE: + value = &db[i]; + + state = VALUE; + + break; + case INVALID_LINE: + if (strchr(NEWLINE, db[i])) + state = PRE_KEY; + + break; + case VALUE: + if (strchr(NEWLINE, db[i])) { + db[i] = '\0'; + r = handle_db_line(device, key, value); + if (r < 0) + log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value); + + state = PRE_KEY; + } + + break; + default: + assert_not_reached("invalid state when parsing db"); + } + } + + device->db_loaded = true; + + return 0; +} + +uint64_t device_get_properties_generation(sd_device *device) { + assert(device); + + return device->properties_generation; +} + +uint64_t device_get_tags_generation(sd_device *device) { + assert(device); + + return device->tags_generation; +} + +uint64_t device_get_devlinks_generation(sd_device *device) { + assert(device); + + return device->devlinks_generation; +} + +int device_get_devnode_mode(sd_device *device, mode_t *mode) { + int r; + + assert(device); + assert(mode); + + r = device_read_db(device); + if (r < 0) + return r; + + *mode = device->devmode; + + return 0; +} + +int device_get_devnode_uid(sd_device *device, uid_t *uid) { + int r; + + assert(device); + assert(uid); + + r = device_read_db(device); + if (r < 0) + return r; + + *uid = device->devuid; + + return 0; +} + +static int device_set_devuid(sd_device *device, const char *uid) { + unsigned u; + int r; + + assert(device); + assert(uid); + + r = safe_atou(uid, &u); + if (r < 0) + return r; + + r = device_add_property_internal(device, "DEVUID", uid); + if (r < 0) + return r; + + device->devuid = u; + + return 0; +} + +int device_get_devnode_gid(sd_device *device, gid_t *gid) { + int r; + + assert(device); + assert(gid); + + r = device_read_db(device); + if (r < 0) + return r; + + *gid = device->devgid; + + return 0; +} + +static int device_set_devgid(sd_device *device, const char *gid) { + unsigned g; + int r; + + assert(device); + assert(gid); + + r = safe_atou(gid, &g); + if (r < 0) + return r; + + r = device_add_property_internal(device, "DEVGID", gid); + if (r < 0) + return r; + + device->devgid = g; + + return 0; +} + +static int device_amend(sd_device *device, const char *key, const char *value) { + int r; + + assert(device); + assert(key); + assert(value); + + if (streq(key, "DEVPATH")) { + char *path; + + path = strjoina("/sys", value); + + /* the caller must verify or trust this data (e.g., if it comes from the kernel) */ + r = device_set_syspath(device, path, false); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set syspath to '%s': %m", path); + } else if (streq(key, "SUBSYSTEM")) { + r = device_set_subsystem(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set subsystem to '%s': %m", value); + } else if (streq(key, "DEVTYPE")) { + r = device_set_devtype(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devtype to '%s': %m", value); + } else if (streq(key, "DEVNAME")) { + r = device_set_devname(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devname to '%s': %m", value); + } else if (streq(key, "USEC_INITIALIZED")) { + r = device_set_usec_initialized(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set usec-initialized to '%s': %m", value); + } else if (streq(key, "DRIVER")) { + r = device_set_driver(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set driver to '%s': %m", value); + } else if (streq(key, "IFINDEX")) { + r = device_set_ifindex(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set ifindex to '%s': %m", value); + } else if (streq(key, "DEVMODE")) { + r = device_set_devmode(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devmode to '%s': %m", value); + } else if (streq(key, "DEVUID")) { + r = device_set_devuid(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devuid to '%s': %m", value); + } else if (streq(key, "DEVGID")) { + r = device_set_devgid(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devgid to '%s': %m", value); + } else if (streq(key, "DEVLINKS")) { + const char *word, *state; + size_t l; + + FOREACH_WORD(word, l, value, state) { + char devlink[l + 1]; + + strncpy(devlink, word, l); + devlink[l] = '\0'; + + r = device_add_devlink(device, devlink); + if (r < 0) + return log_debug_errno(r, "sd-device: could not add devlink '%s': %m", devlink); + } + } else if (streq(key, "TAGS")) { + const char *word, *state; + size_t l; + + FOREACH_WORD_SEPARATOR(word, l, value, ":", state) { + char tag[l + 1]; + + (void)strncpy(tag, word, l); + tag[l] = '\0'; + + r = device_add_tag(device, tag); + if (r < 0) + return log_debug_errno(r, "sd-device: could not add tag '%s': %m", tag); + } + } else { + r = device_add_property_internal(device, key, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not add property '%s=%s': %m", key, value); + } + + return 0; +} + +static const char* const device_action_table[_DEVICE_ACTION_MAX] = { + [DEVICE_ACTION_ADD] = "add", + [DEVICE_ACTION_REMOVE] = "remove", + [DEVICE_ACTION_CHANGE] = "change", + [DEVICE_ACTION_MOVE] = "move", + [DEVICE_ACTION_ONLINE] = "online", + [DEVICE_ACTION_OFFLINE] = "offline", +}; + +DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction); + +static int device_append(sd_device *device, char *key, const char **_major, const char **_minor, uint64_t *_seqnum, + DeviceAction *_action) { + DeviceAction action = _DEVICE_ACTION_INVALID; + uint64_t seqnum = 0; + const char *major = NULL, *minor = NULL; + char *value; + int r; + + assert(device); + assert(key); + assert(_major); + assert(_minor); + assert(_seqnum); + assert(_action); + + value = strchr(key, '='); + if (!value) { + log_debug("sd-device: not a key-value pair: '%s'", key); + return -EINVAL; + } + + *value = '\0'; + + value++; + + if (streq(key, "MAJOR")) + major = value; + else if (streq(key, "MINOR")) + minor = value; + else { + if (streq(key, "ACTION")) { + action = device_action_from_string(value); + if (action == _DEVICE_ACTION_INVALID) + return -EINVAL; + } else if (streq(key, "SEQNUM")) { + r = safe_atou64(value, &seqnum); + if (r < 0) + return r; + else if (seqnum == 0) + /* kernel only sends seqnum > 0 */ + return -EINVAL; + } + + r = device_amend(device, key, value); + if (r < 0) + return r; + } + + if (major != 0) + *_major = major; + + if (minor != 0) + *_minor = minor; + + if (action != _DEVICE_ACTION_INVALID) + *_action = action; + + if (seqnum > 0) + *_seqnum = seqnum; + + return 0; +} + +void device_seal(sd_device *device) { + assert(device); + + device->sealed = true; +} + +static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum) { + assert(device); + + if (!device->devpath || !device->subsystem || action == _DEVICE_ACTION_INVALID || seqnum == 0) { + log_debug("sd-device: device created from strv lacks devpath, subsystem, action or seqnum"); + return -EINVAL; + } + + device->sealed = true; + + return 0; +} + +int device_new_from_strv(sd_device **ret, char **strv) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + char **key; + const char *major = NULL, *minor = NULL; + DeviceAction action = _DEVICE_ACTION_INVALID; + uint64_t seqnum; + int r; + + assert(ret); + assert(strv); + + r = device_new_aux(&device); + if (r < 0) + return r; + + STRV_FOREACH(key, strv) { + r = device_append(device, *key, &major, &minor, &seqnum, &action); + if (r < 0) + return r; + } + + if (major) { + r = device_set_devnum(device, major, minor); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor); + } + + r = device_verify(device, action, seqnum); + if (r < 0) + return r; + + *ret = device; + device = NULL; + + return 0; +} + +int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + const char *major = NULL, *minor = NULL; + DeviceAction action = _DEVICE_ACTION_INVALID; + uint64_t seqnum; + unsigned i = 0; + int r; + + assert(ret); + assert(nulstr); + assert(len); + + r = device_new_aux(&device); + if (r < 0) + return r; + + while (i < len) { + char *key; + const char *end; + + key = (char*)&nulstr[i]; + end = memchr(key, '\0', len - i); + if (!end) { + log_debug("sd-device: failed to parse nulstr"); + return -EINVAL; + } + i += end - key + 1; + + r = device_append(device, key, &major, &minor, &seqnum, &action); + if (r < 0) + return r; + } + + if (major) { + r = device_set_devnum(device, major, minor); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor); + } + + r = device_verify(device, action, seqnum); + if (r < 0) + return r; + + *ret = device; + device = NULL; + + return 0; +} + +static int device_update_properties_bufs(sd_device *device) { + const char *val, *prop; + _cleanup_free_ char **buf_strv = NULL; + _cleanup_free_ uint8_t *buf_nulstr = NULL; + size_t allocated_nulstr = 0; + size_t nulstr_len = 0, num = 0, i = 0; + + assert(device); + + if (!device->properties_buf_outdated) + return 0; + + FOREACH_DEVICE_PROPERTY(device, prop, val) { + size_t len = 0; + + len = strlen(prop) + 1 + strlen(val); + + buf_nulstr = GREEDY_REALLOC0(buf_nulstr, allocated_nulstr, nulstr_len + len + 2); + if (!buf_nulstr) + return -ENOMEM; + + strscpyl((char *)buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL); + nulstr_len += len + 1; + ++num; + } + + /* build buf_strv from buf_nulstr */ + buf_strv = new0(char *, num + 1); + if (!buf_strv) + return -ENOMEM; + + NULSTR_FOREACH(val, (char*) buf_nulstr) { + buf_strv[i] = (char *) val; + assert(i < num); + i++; + } + + free(device->properties_nulstr); + device->properties_nulstr = buf_nulstr; + buf_nulstr = NULL; + device->properties_nulstr_len = nulstr_len; + free(device->properties_strv); + device->properties_strv = buf_strv; + buf_strv = NULL; + + device->properties_buf_outdated = false; + + return 0; +} + +int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len) { + int r; + + assert(device); + assert(nulstr); + assert(len); + + r = device_update_properties_bufs(device); + if (r < 0) + return r; + + *nulstr = device->properties_nulstr; + *len = device->properties_nulstr_len; + + return 0; +} + +int device_get_properties_strv(sd_device *device, char ***strv) { + int r; + + assert(device); + assert(strv); + + r = device_update_properties_bufs(device); + if (r < 0) + return r; + + *strv = device->properties_strv; + + return 0; +} + +int device_get_devlink_priority(sd_device *device, int *priority) { + int r; + + assert(device); + assert(priority); + + r = device_read_db(device); + if (r < 0) + return r; + + *priority = device->devlink_priority; + + return 0; +} + +int device_get_watch_handle(sd_device *device, int *handle) { + int r; + + assert(device); + assert(handle); + + r = device_read_db(device); + if (r < 0) + return r; + + *handle = device->watch_handle; + + return 0; +} + +void device_set_watch_handle(sd_device *device, int handle) { + assert(device); + + device->watch_handle = handle; +} + +int device_rename(sd_device *device, const char *name) { + _cleanup_free_ char *dirname = NULL; + char *new_syspath; + const char *interface; + int r; + + assert(device); + assert(name); + + dirname = dirname_malloc(device->syspath); + if (!dirname) + return -ENOMEM; + + new_syspath = strjoina(dirname, "/", name); + + /* the user must trust that the new name is correct */ + r = device_set_syspath(device, new_syspath, false); + if (r < 0) + return r; + + r = sd_device_get_property_value(device, "INTERFACE", &interface); + if (r >= 0) { + r = device_add_property_internal(device, "INTERFACE", name); + if (r < 0) + return r; + + /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */ + r = device_add_property_internal(device, "INTERFACE_OLD", interface); + if (r < 0) + return r; + } else if (r != -ENOENT) + return r; + + return 0; +} + +int device_shallow_clone(sd_device *old_device, sd_device **new_device) { + _cleanup_(sd_device_unrefp) sd_device *ret = NULL; + int r; + + assert(old_device); + assert(new_device); + + r = device_new_aux(&ret); + if (r < 0) + return r; + + r = device_set_syspath(ret, old_device->syspath, false); + if (r < 0) + return r; + + r = device_set_subsystem(ret, old_device->subsystem); + if (r < 0) + return r; + + ret->devnum = old_device->devnum; + + *new_device = ret; + ret = NULL; + + return 0; +} + +int device_clone_with_db(sd_device *old_device, sd_device **new_device) { + _cleanup_(sd_device_unrefp) sd_device *ret = NULL; + int r; + + assert(old_device); + assert(new_device); + + r = device_shallow_clone(old_device, &ret); + if (r < 0) + return r; + + r = device_read_db(ret); + if (r < 0) + return r; + + ret->sealed = true; + + *new_device = ret; + ret = NULL; + + return 0; +} + +int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) { + _cleanup_(sd_device_unrefp) sd_device *ret = NULL; + int r; + + assert(new_device); + assert(syspath); + assert(action); + + r = sd_device_new_from_syspath(&ret, syspath); + if (r < 0) + return r; + + r = device_read_uevent_file(ret); + if (r < 0) + return r; + + r = device_add_property_internal(ret, "ACTION", action); + if (r < 0) + return r; + + *new_device = ret; + ret = NULL; + + return 0; +} + +int device_copy_properties(sd_device *device_dst, sd_device *device_src) { + const char *property, *value; + int r; + + assert(device_dst); + assert(device_src); + + FOREACH_DEVICE_PROPERTY(device_src, property, value) { + r = device_add_property(device_dst, property, value); + if (r < 0) + return r; + } + + return 0; +} + +void device_cleanup_tags(sd_device *device) { + assert(device); + + set_free_free(device->tags); + device->tags = NULL; + device->property_tags_outdated = true; + device->tags_generation++; +} + +void device_cleanup_devlinks(sd_device *device) { + assert(device); + + set_free_free(device->devlinks); + device->devlinks = NULL; + device->property_devlinks_outdated = true; + device->devlinks_generation++; +} + +void device_remove_tag(sd_device *device, const char *tag) { + assert(device); + assert(tag); + + free(set_remove(device->tags, tag)); + device->property_tags_outdated = true; + device->tags_generation++; +} + +static int device_tag(sd_device *device, const char *tag, bool add) { + const char *id; + char *path; + int r; + + assert(device); + assert(tag); + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/tags/", tag, "/", id); + + if (add) { + r = touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444); + if (r < 0) + return r; + } else { + r = unlink(path); + if (r < 0 && errno != ENOENT) + return -errno; + } + + return 0; +} + +int device_tag_index(sd_device *device, sd_device *device_old, bool add) { + const char *tag; + int r = 0, k; + + if (add && device_old) { + /* delete possible left-over tags */ + FOREACH_DEVICE_TAG(device_old, tag) { + if (!sd_device_has_tag(device, tag)) { + k = device_tag(device_old, tag, false); + if (r >= 0 && k < 0) + r = k; + } + } + } + + FOREACH_DEVICE_TAG(device, tag) { + k = device_tag(device, tag, add); + if (r >= 0 && k < 0) + r = k; + } + + return r; +} + +static bool device_has_info(sd_device *device) { + assert(device); + + if (!set_isempty(device->devlinks)) + return true; + + if (device->devlink_priority != 0) + return true; + + if (!ordered_hashmap_isempty(device->properties_db)) + return true; + + if (!set_isempty(device->tags)) + return true; + + if (device->watch_handle >= 0) + return true; + + return false; +} + +void device_set_db_persist(sd_device *device) { + assert(device); + + device->db_persist = true; +} + +int device_update_db(sd_device *device) { + const char *id; + char *path; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *path_tmp = NULL; + bool has_info; + int r; + + assert(device); + + has_info = device_has_info(device); + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + /* do not store anything for otherwise empty devices */ + if (!has_info && major(device->devnum) == 0 && device->ifindex == 0) { + r = unlink(path); + if (r < 0 && errno != ENOENT) + return -errno; + + return 0; + } + + /* write a database file */ + r = mkdir_parents(path, 0755); + if (r < 0) + return r; + + r = fopen_temporary(path, &f, &path_tmp); + if (r < 0) + return r; + + /* + * set 'sticky' bit to indicate that we should not clean the + * database when we transition from initramfs to the real root + */ + if (device->db_persist) { + r = fchmod(fileno(f), 01644); + if (r < 0) { + r = -errno; + goto fail; + } + } else { + r = fchmod(fileno(f), 0644); + if (r < 0) { + r = -errno; + goto fail; + } + } + + if (has_info) { + const char *property, *value, *tag; + Iterator i; + + if (major(device->devnum) > 0) { + const char *devlink; + + FOREACH_DEVICE_DEVLINK(device, devlink) + fprintf(f, "S:%s\n", devlink + strlen("/dev/")); + + if (device->devlink_priority != 0) + fprintf(f, "L:%i\n", device->devlink_priority); + + if (device->watch_handle >= 0) + fprintf(f, "W:%i\n", device->watch_handle); + } + + if (device->usec_initialized > 0) + fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized); + + ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db, i) + fprintf(f, "E:%s=%s\n", property, value); + + FOREACH_DEVICE_TAG(device, tag) + fprintf(f, "G:%s\n", tag); + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + r = rename(path_tmp, path); + if (r < 0) { + r = -errno; + goto fail; + } + + log_debug("created %s file '%s' for '%s'", has_info ? "db" : "empty", + path, device->devpath); + + return 0; + +fail: + (void) unlink(path); + (void) unlink(path_tmp); + + return log_error_errno(r, "failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", path, device->devpath); +} + +int device_delete_db(sd_device *device) { + const char *id; + char *path; + int r; + + assert(device); + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + r = unlink(path); + if (r < 0 && errno != ENOENT) + return -errno; + + return 0; +} + +int device_read_db_force(sd_device *device) { + assert(device); + + return device_read_db_aux(device, true); +} diff --git a/src/libsystemd/src/sd-device/device-private.h b/src/libsystemd/src/sd-device/device-private.h new file mode 100644 index 0000000000..d6add2f7b2 --- /dev/null +++ b/src/libsystemd/src/sd-device/device-private.h @@ -0,0 +1,68 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen + + 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 . +***/ + +#include +#include +#include + +#include + +int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len); +int device_new_from_strv(sd_device **ret, char **strv); + +int device_get_id_filename(sd_device *device, const char **ret); + +int device_get_devlink_priority(sd_device *device, int *priority); +int device_get_watch_handle(sd_device *device, int *handle); +int device_get_devnode_mode(sd_device *device, mode_t *mode); +int device_get_devnode_uid(sd_device *device, uid_t *uid); +int device_get_devnode_gid(sd_device *device, gid_t *gid); + +void device_seal(sd_device *device); +void device_set_is_initialized(sd_device *device); +void device_set_watch_handle(sd_device *device, int fd); +void device_set_db_persist(sd_device *device); +void device_set_devlink_priority(sd_device *device, int priority); +int device_ensure_usec_initialized(sd_device *device, sd_device *device_old); +int device_add_devlink(sd_device *device, const char *devlink); +int device_add_property(sd_device *device, const char *property, const char *value); +int device_add_tag(sd_device *device, const char *tag); +void device_remove_tag(sd_device *device, const char *tag); +void device_cleanup_tags(sd_device *device); +void device_cleanup_devlinks(sd_device *device); + +uint64_t device_get_properties_generation(sd_device *device); +uint64_t device_get_tags_generation(sd_device *device); +uint64_t device_get_devlinks_generation(sd_device *device); + +int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len); +int device_get_properties_strv(sd_device *device, char ***strv); + +int device_rename(sd_device *device, const char *name); +int device_shallow_clone(sd_device *old_device, sd_device **new_device); +int device_clone_with_db(sd_device *old_device, sd_device **new_device); +int device_copy_properties(sd_device *device_dst, sd_device *device_src); +int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action); + +int device_tag_index(sd_device *dev, sd_device *dev_old, bool add); +int device_update_db(sd_device *device); +int device_delete_db(sd_device *device); +int device_read_db_force(sd_device *device); diff --git a/src/libsystemd/src/sd-device/device-util.h b/src/libsystemd/src/sd-device/device-util.h new file mode 100644 index 0000000000..5b42e11de6 --- /dev/null +++ b/src/libsystemd/src/sd-device/device-util.h @@ -0,0 +1,52 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014-2015 Tom Gundersen + + 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 . +***/ + +#include "util.h" + +#define FOREACH_DEVICE_PROPERTY(device, key, value) \ + for (key = sd_device_get_property_first(device, &(value)); \ + key; \ + key = sd_device_get_property_next(device, &(value))) + +#define FOREACH_DEVICE_TAG(device, tag) \ + for (tag = sd_device_get_tag_first(device); \ + tag; \ + tag = sd_device_get_tag_next(device)) + +#define FOREACH_DEVICE_SYSATTR(device, attr) \ + for (attr = sd_device_get_sysattr_first(device); \ + attr; \ + attr = sd_device_get_sysattr_next(device)) + +#define FOREACH_DEVICE_DEVLINK(device, devlink) \ + for (devlink = sd_device_get_devlink_first(device); \ + devlink; \ + devlink = sd_device_get_devlink_next(device)) + +#define FOREACH_DEVICE(enumerator, device) \ + for (device = sd_device_enumerator_get_device_first(enumerator); \ + device; \ + device = sd_device_enumerator_get_device_next(enumerator)) + +#define FOREACH_SUBSYSTEM(enumerator, device) \ + for (device = sd_device_enumerator_get_subsystem_first(enumerator); \ + device; \ + device = sd_device_enumerator_get_subsystem_next(enumerator)) diff --git a/src/libsystemd/src/sd-device/sd-device.c b/src/libsystemd/src/sd-device/sd-device.c new file mode 100644 index 0000000000..d21a67cd4c --- /dev/null +++ b/src/libsystemd/src/sd-device/sd-device.c @@ -0,0 +1,1884 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + Copyright 2014 Tom Gundersen + + 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 . +***/ + +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "device-internal.h" +#include "device-private.h" +#include "device-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "hashmap.h" +#include "macro.h" +#include "parse-util.h" +#include "path-util.h" +#include "set.h" +#include "stat-util.h" +#include "string-util.h" +#include "strv.h" +#include "strxcpyx.h" +#include "util.h" + +int device_new_aux(sd_device **ret) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + + assert(ret); + + device = new0(sd_device, 1); + if (!device) + return -ENOMEM; + + device->n_ref = 1; + device->watch_handle = -1; + + *ret = device; + device = NULL; + + return 0; +} + +_public_ sd_device *sd_device_ref(sd_device *device) { + if (device) + assert_se(++ device->n_ref >= 2); + + return device; +} + +_public_ sd_device *sd_device_unref(sd_device *device) { + if (device && -- device->n_ref == 0) { + sd_device_unref(device->parent); + free(device->syspath); + free(device->sysname); + free(device->devtype); + free(device->devname); + free(device->subsystem); + free(device->driver); + free(device->id_filename); + free(device->properties_strv); + free(device->properties_nulstr); + + ordered_hashmap_free_free_free(device->properties); + ordered_hashmap_free_free_free(device->properties_db); + hashmap_free_free_free(device->sysattr_values); + set_free_free(device->sysattrs); + set_free_free(device->tags); + set_free_free(device->devlinks); + + free(device); + } + + return NULL; +} + +int device_add_property_aux(sd_device *device, const char *_key, const char *_value, bool db) { + OrderedHashmap **properties; + + assert(device); + assert(_key); + + if (db) + properties = &device->properties_db; + else + properties = &device->properties; + + if (_value) { + _cleanup_free_ char *key = NULL, *value = NULL, *old_key = NULL, *old_value = NULL; + int r; + + r = ordered_hashmap_ensure_allocated(properties, &string_hash_ops); + if (r < 0) + return r; + + key = strdup(_key); + if (!key) + return -ENOMEM; + + value = strdup(_value); + if (!value) + return -ENOMEM; + + old_value = ordered_hashmap_get2(*properties, key, (void**) &old_key); + + r = ordered_hashmap_replace(*properties, key, value); + if (r < 0) + return r; + + key = NULL; + value = NULL; + } else { + _cleanup_free_ char *key = NULL; + _cleanup_free_ char *value = NULL; + + value = ordered_hashmap_remove2(*properties, _key, (void**) &key); + } + + if (!db) { + device->properties_generation++; + device->properties_buf_outdated = true; + } + + return 0; +} + +int device_add_property_internal(sd_device *device, const char *key, const char *value) { + return device_add_property_aux(device, key, value, false); +} + +int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { + _cleanup_free_ char *syspath = NULL; + const char *devpath; + int r; + + assert(device); + assert(_syspath); + + /* must be a subdirectory of /sys */ + if (!path_startswith(_syspath, "/sys/")) { + log_debug("sd-device: syspath '%s' is not a subdirectory of /sys", _syspath); + return -EINVAL; + } + + if (verify) { + r = readlink_and_canonicalize(_syspath, &syspath); + if (r == -ENOENT) + /* the device does not exist (any more?) */ + return -ENODEV; + else if (r == -EINVAL) { + /* not a symlink */ + syspath = canonicalize_file_name(_syspath); + if (!syspath) { + if (errno == ENOENT) + /* the device does not exist (any more?) */ + return -ENODEV; + + return log_debug_errno(errno, "sd-device: could not canonicalize '%s': %m", _syspath); + } + } else if (r < 0) { + log_debug_errno(r, "sd-device: could not get target of '%s': %m", _syspath); + return r; + } + + if (path_startswith(syspath, "/sys/devices/")) { + char *path; + + /* all 'devices' require an 'uevent' file */ + path = strjoina(syspath, "/uevent"); + r = access(path, F_OK); + if (r < 0) { + if (errno == ENOENT) + /* this is not a valid device */ + return -ENODEV; + + log_debug("sd-device: %s does not have an uevent file: %m", syspath); + return -errno; + } + } else { + /* everything else just just needs to be a directory */ + if (!is_dir(syspath, false)) + return -ENODEV; + } + } else { + syspath = strdup(_syspath); + if (!syspath) + return -ENOMEM; + } + + devpath = syspath + strlen("/sys"); + + r = device_add_property_internal(device, "DEVPATH", devpath); + if (r < 0) + return r; + + free(device->syspath); + device->syspath = syspath; + syspath = NULL; + + device->devpath = devpath; + + return 0; +} + +_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + int r; + + assert_return(ret, -EINVAL); + assert_return(syspath, -EINVAL); + + r = device_new_aux(&device); + if (r < 0) + return r; + + r = device_set_syspath(device, syspath, true); + if (r < 0) + return r; + + *ret = device; + device = NULL; + + return 0; +} + +_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) { + char *syspath; + char id[DECIMAL_STR_MAX(unsigned) * 2 + 1]; + + assert_return(ret, -EINVAL); + assert_return(type == 'b' || type == 'c', -EINVAL); + + /* use /sys/dev/{block,char}/: link */ + snprintf(id, sizeof(id), "%u:%u", major(devnum), minor(devnum)); + + syspath = strjoina("/sys/dev/", (type == 'b' ? "block" : "char"), "/", id); + + return sd_device_new_from_syspath(ret, syspath); +} + +_public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname) { + char *syspath; + + assert_return(ret, -EINVAL); + assert_return(subsystem, -EINVAL); + assert_return(sysname, -EINVAL); + + if (streq(subsystem, "subsystem")) { + syspath = strjoina("/sys/subsystem/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/bus/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/class/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + } else if (streq(subsystem, "module")) { + syspath = strjoina("/sys/module/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + } else if (streq(subsystem, "drivers")) { + char subsys[PATH_MAX]; + char *driver; + + strscpy(subsys, sizeof(subsys), sysname); + driver = strchr(subsys, ':'); + if (driver) { + driver[0] = '\0'; + driver++; + + syspath = strjoina("/sys/subsystem/", subsys, "/drivers/", driver); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/bus/", subsys, "/drivers/", driver); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + } else + return -EINVAL; + } else { + char *name; + size_t len = 0; + + /* translate sysname back to sysfs filename */ + name = strdupa(sysname); + while (name[len] != '\0') { + if (name[len] == '/') + name[len] = '!'; + + len++; + } + + syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", name); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/bus/", subsystem, "/devices/", name); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/class/", subsystem, "/", name); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + } + + return -ENODEV; +} + +int device_set_devtype(sd_device *device, const char *_devtype) { + _cleanup_free_ char *devtype = NULL; + int r; + + assert(device); + assert(_devtype); + + devtype = strdup(_devtype); + if (!devtype) + return -ENOMEM; + + r = device_add_property_internal(device, "DEVTYPE", devtype); + if (r < 0) + return r; + + free(device->devtype); + device->devtype = devtype; + devtype = NULL; + + return 0; +} + +int device_set_ifindex(sd_device *device, const char *_ifindex) { + int ifindex, r; + + assert(device); + assert(_ifindex); + + r = parse_ifindex(_ifindex, &ifindex); + if (r < 0) + return r; + + r = device_add_property_internal(device, "IFINDEX", _ifindex); + if (r < 0) + return r; + + device->ifindex = ifindex; + + return 0; +} + +int device_set_devname(sd_device *device, const char *_devname) { + _cleanup_free_ char *devname = NULL; + int r; + + assert(device); + assert(_devname); + + if (_devname[0] != '/') { + r = asprintf(&devname, "/dev/%s", _devname); + if (r < 0) + return -ENOMEM; + } else { + devname = strdup(_devname); + if (!devname) + return -ENOMEM; + } + + r = device_add_property_internal(device, "DEVNAME", devname); + if (r < 0) + return r; + + free(device->devname); + device->devname = devname; + devname = NULL; + + return 0; +} + +int device_set_devmode(sd_device *device, const char *_devmode) { + unsigned devmode; + int r; + + assert(device); + assert(_devmode); + + r = safe_atou(_devmode, &devmode); + if (r < 0) + return r; + + if (devmode > 07777) + return -EINVAL; + + r = device_add_property_internal(device, "DEVMODE", _devmode); + if (r < 0) + return r; + + device->devmode = devmode; + + return 0; +} + +int device_set_devnum(sd_device *device, const char *major, const char *minor) { + unsigned maj = 0, min = 0; + int r; + + assert(device); + assert(major); + + r = safe_atou(major, &maj); + if (r < 0) + return r; + if (!maj) + return 0; + + if (minor) { + r = safe_atou(minor, &min); + if (r < 0) + return r; + } + + r = device_add_property_internal(device, "MAJOR", major); + if (r < 0) + return r; + + if (minor) { + r = device_add_property_internal(device, "MINOR", minor); + if (r < 0) + return r; + } + + device->devnum = makedev(maj, min); + + return 0; +} + +static int handle_uevent_line(sd_device *device, const char *key, const char *value, const char **major, const char **minor) { + int r; + + assert(device); + assert(key); + assert(value); + assert(major); + assert(minor); + + if (streq(key, "DEVTYPE")) { + r = device_set_devtype(device, value); + if (r < 0) + return r; + } else if (streq(key, "IFINDEX")) { + r = device_set_ifindex(device, value); + if (r < 0) + return r; + } else if (streq(key, "DEVNAME")) { + r = device_set_devname(device, value); + if (r < 0) + return r; + } else if (streq(key, "DEVMODE")) { + r = device_set_devmode(device, value); + if (r < 0) + return r; + } else if (streq(key, "MAJOR")) + *major = value; + else if (streq(key, "MINOR")) + *minor = value; + else { + r = device_add_property_internal(device, key, value); + if (r < 0) + return r; + } + + return 0; +} + +int device_read_uevent_file(sd_device *device) { + _cleanup_free_ char *uevent = NULL; + const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL; + char *path; + size_t uevent_len; + unsigned i; + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + INVALID_LINE, + } state = PRE_KEY; + + assert(device); + + if (device->uevent_loaded || device->sealed) + return 0; + + device->uevent_loaded = true; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/uevent"); + + r = read_full_file(path, &uevent, &uevent_len); + if (r == -EACCES) + /* empty uevent files may be write-only */ + return 0; + else if (r == -ENOENT) + /* some devices may not have uevent files, see set_syspath() */ + return 0; + else if (r < 0) { + log_debug_errno(r, "sd-device: failed to read uevent file '%s': %m", path); + return r; + } + + for (i = 0; i < uevent_len; i++) { + switch (state) { + case PRE_KEY: + if (!strchr(NEWLINE, uevent[i])) { + key = &uevent[i]; + + state = KEY; + } + + break; + case KEY: + if (uevent[i] == '=') { + uevent[i] = '\0'; + + state = PRE_VALUE; + } else if (strchr(NEWLINE, uevent[i])) { + uevent[i] = '\0'; + log_debug("sd-device: ignoring invalid uevent line '%s'", key); + + state = PRE_KEY; + } + + break; + case PRE_VALUE: + value = &uevent[i]; + + state = VALUE; + + break; + case VALUE: + if (strchr(NEWLINE, uevent[i])) { + uevent[i] = '\0'; + + r = handle_uevent_line(device, key, value, &major, &minor); + if (r < 0) + log_debug_errno(r, "sd-device: failed to handle uevent entry '%s=%s': %m", key, value); + + state = PRE_KEY; + } + + break; + default: + assert_not_reached("invalid state when parsing uevent file"); + } + } + + if (major) { + r = device_set_devnum(device, major, minor); + if (r < 0) + log_debug_errno(r, "sd-device: could not set 'MAJOR=%s' or 'MINOR=%s' from '%s': %m", major, minor, path); + } + + return 0; +} + +_public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) { + int r; + + assert_return(device, -EINVAL); + assert_return(ifindex, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + *ifindex = device->ifindex; + + return 0; +} + +_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) { + int r; + + assert_return(ret, -EINVAL); + assert_return(id, -EINVAL); + + switch (id[0]) { + case 'b': + case 'c': + { + char type; + int maj, min; + + r = sscanf(id, "%c%i:%i", &type, &maj, &min); + if (r != 3) + return -EINVAL; + + return sd_device_new_from_devnum(ret, type, makedev(maj, min)); + } + case 'n': + { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + _cleanup_close_ int sk = -1; + struct ifreq ifr = {}; + int ifindex; + + r = parse_ifindex(&id[1], &ifr.ifr_ifindex); + if (r < 0) + return r; + + sk = socket(PF_INET, SOCK_DGRAM, 0); + if (sk < 0) + return -errno; + + r = ioctl(sk, SIOCGIFNAME, &ifr); + if (r < 0) + return -errno; + + r = sd_device_new_from_subsystem_sysname(&device, "net", ifr.ifr_name); + if (r < 0) + return r; + + r = sd_device_get_ifindex(device, &ifindex); + if (r < 0) + return r; + + /* this is racey, so we might end up with the wrong device */ + if (ifr.ifr_ifindex != ifindex) + return -ENODEV; + + *ret = device; + device = NULL; + + return 0; + } + case '+': + { + char subsys[PATH_MAX]; + char *sysname; + + (void)strscpy(subsys, sizeof(subsys), id + 1); + sysname = strchr(subsys, ':'); + if (!sysname) + return -EINVAL; + + sysname[0] = '\0'; + sysname++; + + return sd_device_new_from_subsystem_sysname(ret, subsys, sysname); + } + default: + return -EINVAL; + } +} + +_public_ int sd_device_get_syspath(sd_device *device, const char **ret) { + assert_return(device, -EINVAL); + assert_return(ret, -EINVAL); + + assert(path_startswith(device->syspath, "/sys/")); + + *ret = device->syspath; + + return 0; +} + +static int device_new_from_child(sd_device **ret, sd_device *child) { + _cleanup_free_ char *path = NULL; + const char *subdir, *syspath; + int r; + + assert(ret); + assert(child); + + r = sd_device_get_syspath(child, &syspath); + if (r < 0) + return r; + + path = strdup(syspath); + if (!path) + return -ENOMEM; + subdir = path + strlen("/sys"); + + for (;;) { + char *pos; + + pos = strrchr(subdir, '/'); + if (!pos || pos < subdir + 2) + break; + + *pos = '\0'; + + r = sd_device_new_from_syspath(ret, path); + if (r < 0) + continue; + + return 0; + } + + return -ENODEV; +} + +_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) { + + assert_return(ret, -EINVAL); + assert_return(child, -EINVAL); + + if (!child->parent_set) { + child->parent_set = true; + + (void)device_new_from_child(&child->parent, child); + } + + if (!child->parent) + return -ENOENT; + + *ret = child->parent; + + return 0; +} + +int device_set_subsystem(sd_device *device, const char *_subsystem) { + _cleanup_free_ char *subsystem = NULL; + int r; + + assert(device); + assert(_subsystem); + + subsystem = strdup(_subsystem); + if (!subsystem) + return -ENOMEM; + + r = device_add_property_internal(device, "SUBSYSTEM", subsystem); + if (r < 0) + return r; + + free(device->subsystem); + device->subsystem = subsystem; + subsystem = NULL; + + device->subsystem_set = true; + + return 0; +} + +_public_ int sd_device_get_subsystem(sd_device *device, const char **ret) { + assert_return(ret, -EINVAL); + assert_return(device, -EINVAL); + + if (!device->subsystem_set) { + _cleanup_free_ char *subsystem = NULL; + const char *syspath; + char *path; + int r; + + /* read 'subsystem' link */ + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/subsystem"); + r = readlink_value(path, &subsystem); + if (r >= 0) + r = device_set_subsystem(device, subsystem); + /* use implicit names */ + else if (path_startswith(device->devpath, "/module/")) + r = device_set_subsystem(device, "module"); + else if (strstr(device->devpath, "/drivers/")) + r = device_set_subsystem(device, "drivers"); + else if (path_startswith(device->devpath, "/subsystem/") || + path_startswith(device->devpath, "/class/") || + path_startswith(device->devpath, "/bus/")) + r = device_set_subsystem(device, "subsystem"); + if (r < 0 && r != -ENOENT) + return log_debug_errno(r, "sd-device: could not set subsystem for %s: %m", device->devpath); + + device->subsystem_set = true; + } + + if (!device->subsystem) + return -ENOENT; + + *ret = device->subsystem; + + return 0; +} + +_public_ int sd_device_get_devtype(sd_device *device, const char **devtype) { + int r; + + assert(devtype); + assert(device); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + *devtype = device->devtype; + + return 0; +} + +_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) { + sd_device *parent = NULL; + int r; + + assert_return(child, -EINVAL); + assert_return(subsystem, -EINVAL); + + r = sd_device_get_parent(child, &parent); + while (r >= 0) { + const char *parent_subsystem = NULL; + const char *parent_devtype = NULL; + + (void)sd_device_get_subsystem(parent, &parent_subsystem); + if (streq_ptr(parent_subsystem, subsystem)) { + if (!devtype) + break; + + (void)sd_device_get_devtype(parent, &parent_devtype); + if (streq_ptr(parent_devtype, devtype)) + break; + } + r = sd_device_get_parent(parent, &parent); + } + + if (r < 0) + return r; + + *ret = parent; + + return 0; +} + +_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) { + int r; + + assert_return(device, -EINVAL); + assert_return(devnum, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + *devnum = device->devnum; + + return 0; +} + +int device_set_driver(sd_device *device, const char *_driver) { + _cleanup_free_ char *driver = NULL; + int r; + + assert(device); + assert(_driver); + + driver = strdup(_driver); + if (!driver) + return -ENOMEM; + + r = device_add_property_internal(device, "DRIVER", driver); + if (r < 0) + return r; + + free(device->driver); + device->driver = driver; + driver = NULL; + + device->driver_set = true; + + return 0; +} + +_public_ int sd_device_get_driver(sd_device *device, const char **ret) { + assert_return(device, -EINVAL); + assert_return(ret, -EINVAL); + + if (!device->driver_set) { + _cleanup_free_ char *driver = NULL; + const char *syspath; + char *path; + int r; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/driver"); + r = readlink_value(path, &driver); + if (r >= 0) { + r = device_set_driver(device, driver); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath); + } else if (r == -ENOENT) + device->driver_set = true; + else + return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath); + } + + if (!device->driver) + return -ENOENT; + + *ret = device->driver; + + return 0; +} + +_public_ int sd_device_get_devpath(sd_device *device, const char **devpath) { + assert_return(device, -EINVAL); + assert_return(devpath, -EINVAL); + + assert(device->devpath); + assert(device->devpath[0] == '/'); + + *devpath = device->devpath; + + return 0; +} + +_public_ int sd_device_get_devname(sd_device *device, const char **devname) { + int r; + + assert_return(device, -EINVAL); + assert_return(devname, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + if (!device->devname) + return -ENOENT; + + assert(path_startswith(device->devname, "/dev/")); + + *devname = device->devname; + + return 0; +} + +static int device_set_sysname(sd_device *device) { + _cleanup_free_ char *sysname = NULL; + const char *sysnum = NULL; + const char *pos; + size_t len = 0; + + pos = strrchr(device->devpath, '/'); + if (!pos) + return -EINVAL; + pos++; + + /* devpath is not a root directory */ + if (*pos == '\0' || pos <= device->devpath) + return -EINVAL; + + sysname = strdup(pos); + if (!sysname) + return -ENOMEM; + + /* some devices have '!' in their name, change that to '/' */ + while (sysname[len] != '\0') { + if (sysname[len] == '!') + sysname[len] = '/'; + + len++; + } + + /* trailing number */ + while (len > 0 && isdigit(sysname[--len])) + sysnum = &sysname[len]; + + if (len == 0) + sysnum = NULL; + + free(device->sysname); + device->sysname = sysname; + sysname = NULL; + + device->sysnum = sysnum; + + device->sysname_set = true; + + return 0; +} + +_public_ int sd_device_get_sysname(sd_device *device, const char **ret) { + int r; + + assert_return(device, -EINVAL); + assert_return(ret, -EINVAL); + + if (!device->sysname_set) { + r = device_set_sysname(device); + if (r < 0) + return r; + } + + assert_return(device->sysname, -ENOENT); + + *ret = device->sysname; + + return 0; +} + +_public_ int sd_device_get_sysnum(sd_device *device, const char **ret) { + int r; + + assert_return(device, -EINVAL); + assert_return(ret, -EINVAL); + + if (!device->sysname_set) { + r = device_set_sysname(device); + if (r < 0) + return r; + } + + *ret = device->sysnum; + + return 0; +} + +static bool is_valid_tag(const char *tag) { + assert(tag); + + return !strchr(tag, ':') && !strchr(tag, ' '); +} + +int device_add_tag(sd_device *device, const char *tag) { + int r; + + assert(device); + assert(tag); + + if (!is_valid_tag(tag)) + return -EINVAL; + + r = set_ensure_allocated(&device->tags, &string_hash_ops); + if (r < 0) + return r; + + r = set_put_strdup(device->tags, tag); + if (r < 0) + return r; + + device->tags_generation++; + device->property_tags_outdated = true; + + return 0; +} + +int device_add_devlink(sd_device *device, const char *devlink) { + int r; + + assert(device); + assert(devlink); + + r = set_ensure_allocated(&device->devlinks, &string_hash_ops); + if (r < 0) + return r; + + r = set_put_strdup(device->devlinks, devlink); + if (r < 0) + return r; + + device->devlinks_generation++; + device->property_devlinks_outdated = true; + + return 0; +} + +static int device_add_property_internal_from_string(sd_device *device, const char *str) { + _cleanup_free_ char *key = NULL; + char *value; + + assert(device); + assert(str); + + key = strdup(str); + if (!key) + return -ENOMEM; + + value = strchr(key, '='); + if (!value) + return -EINVAL; + + *value = '\0'; + + if (isempty(++value)) + value = NULL; + + return device_add_property_internal(device, key, value); +} + +int device_set_usec_initialized(sd_device *device, const char *initialized) { + uint64_t usec_initialized; + int r; + + assert(device); + assert(initialized); + + r = safe_atou64(initialized, &usec_initialized); + if (r < 0) + return r; + + r = device_add_property_internal(device, "USEC_INITIALIZED", initialized); + if (r < 0) + return r; + + device->usec_initialized = usec_initialized; + + return 0; +} + +static int handle_db_line(sd_device *device, char key, const char *value) { + char *path; + int r; + + assert(device); + assert(value); + + switch (key) { + case 'G': + r = device_add_tag(device, value); + if (r < 0) + return r; + + break; + case 'S': + path = strjoina("/dev/", value); + r = device_add_devlink(device, path); + if (r < 0) + return r; + + break; + case 'E': + r = device_add_property_internal_from_string(device, value); + if (r < 0) + return r; + + break; + case 'I': + r = device_set_usec_initialized(device, value); + if (r < 0) + return r; + + break; + case 'L': + r = safe_atoi(value, &device->devlink_priority); + if (r < 0) + return r; + + break; + case 'W': + r = safe_atoi(value, &device->watch_handle); + if (r < 0) + return r; + + break; + default: + log_debug("device db: unknown key '%c'", key); + } + + return 0; +} + +int device_get_id_filename(sd_device *device, const char **ret) { + assert(device); + assert(ret); + + if (!device->id_filename) { + _cleanup_free_ char *id = NULL; + const char *subsystem; + dev_t devnum; + int ifindex, r; + + r = sd_device_get_subsystem(device, &subsystem); + if (r < 0) + return r; + + r = sd_device_get_devnum(device, &devnum); + if (r < 0) + return r; + + r = sd_device_get_ifindex(device, &ifindex); + if (r < 0) + return r; + + if (major(devnum) > 0) { + assert(subsystem); + + /* use dev_t — b259:131072, c254:0 */ + r = asprintf(&id, "%c%u:%u", + streq(subsystem, "block") ? 'b' : 'c', + major(devnum), minor(devnum)); + if (r < 0) + return -ENOMEM; + } else if (ifindex > 0) { + /* use netdev ifindex — n3 */ + r = asprintf(&id, "n%u", ifindex); + if (r < 0) + return -ENOMEM; + } else { + /* use $subsys:$sysname — pci:0000:00:1f.2 + * sysname() has '!' translated, get it from devpath + */ + const char *sysname; + + sysname = basename(device->devpath); + if (!sysname) + return -EINVAL; + + if (!subsystem) + return -EINVAL; + + r = asprintf(&id, "+%s:%s", subsystem, sysname); + if (r < 0) + return -ENOMEM; + } + + device->id_filename = id; + id = NULL; + } + + *ret = device->id_filename; + + return 0; +} + +int device_read_db_aux(sd_device *device, bool force) { + _cleanup_free_ char *db = NULL; + char *path; + const char *id, *value; + char key; + size_t db_len; + unsigned i; + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + INVALID_LINE, + } state = PRE_KEY; + + if (device->db_loaded || (!force && device->sealed)) + return 0; + + device->db_loaded = true; + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + r = read_full_file(path, &db, &db_len); + if (r < 0) { + if (r == -ENOENT) + return 0; + else + return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path); + } + + /* devices with a database entry are initialized */ + device->is_initialized = true; + + for (i = 0; i < db_len; i++) { + switch (state) { + case PRE_KEY: + if (!strchr(NEWLINE, db[i])) { + key = db[i]; + + state = KEY; + } + + break; + case KEY: + if (db[i] != ':') { + log_debug("sd-device: ignoring invalid db entry with key '%c'", key); + + state = INVALID_LINE; + } else { + db[i] = '\0'; + + state = PRE_VALUE; + } + + break; + case PRE_VALUE: + value = &db[i]; + + state = VALUE; + + break; + case INVALID_LINE: + if (strchr(NEWLINE, db[i])) + state = PRE_KEY; + + break; + case VALUE: + if (strchr(NEWLINE, db[i])) { + db[i] = '\0'; + r = handle_db_line(device, key, value); + if (r < 0) + log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value); + + state = PRE_KEY; + } + + break; + default: + assert_not_reached("invalid state when parsing db"); + } + } + + return 0; +} + +static int device_read_db(sd_device *device) { + return device_read_db_aux(device, false); +} + +_public_ int sd_device_get_is_initialized(sd_device *device, int *initialized) { + int r; + + assert_return(device, -EINVAL); + assert_return(initialized, -EINVAL); + + r = device_read_db(device); + if (r < 0) + return r; + + *initialized = device->is_initialized; + + return 0; +} + +_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec) { + usec_t now_ts; + int r; + + assert_return(device, -EINVAL); + assert_return(usec, -EINVAL); + + r = device_read_db(device); + if (r < 0) + return r; + + if (!device->is_initialized) + return -EBUSY; + + if (!device->usec_initialized) + return -ENODATA; + + now_ts = now(clock_boottime_or_monotonic()); + + if (now_ts < device->usec_initialized) + return -EIO; + + *usec = now_ts - device->usec_initialized; + + return 0; +} + +_public_ const char *sd_device_get_tag_first(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + device->tags_iterator_generation = device->tags_generation; + device->tags_iterator = ITERATOR_FIRST; + + (void) set_iterate(device->tags, &device->tags_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_tag_next(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + if (device->tags_iterator_generation != device->tags_generation) + return NULL; + + (void) set_iterate(device->tags, &device->tags_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_devlink_first(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + device->devlinks_iterator_generation = device->devlinks_generation; + device->devlinks_iterator = ITERATOR_FIRST; + + (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_devlink_next(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + if (device->devlinks_iterator_generation != device->devlinks_generation) + return NULL; + + (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v); + return v; +} + +static int device_properties_prepare(sd_device *device) { + int r; + + assert(device); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + r = device_read_db(device); + if (r < 0) + return r; + + if (device->property_devlinks_outdated) { + _cleanup_free_ char *devlinks = NULL; + size_t devlinks_allocated = 0, devlinks_len = 0; + const char *devlink; + + for (devlink = sd_device_get_devlink_first(device); devlink; devlink = sd_device_get_devlink_next(device)) { + char *e; + + if (!GREEDY_REALLOC(devlinks, devlinks_allocated, devlinks_len + strlen(devlink) + 2)) + return -ENOMEM; + if (devlinks_len > 0) + stpcpy(devlinks + devlinks_len++, " "); + e = stpcpy(devlinks + devlinks_len, devlink); + devlinks_len = e - devlinks; + } + + r = device_add_property_internal(device, "DEVLINKS", devlinks); + if (r < 0) + return r; + + device->property_devlinks_outdated = false; + } + + if (device->property_tags_outdated) { + _cleanup_free_ char *tags = NULL; + size_t tags_allocated = 0, tags_len = 0; + const char *tag; + + if (!GREEDY_REALLOC(tags, tags_allocated, 2)) + return -ENOMEM; + stpcpy(tags, ":"); + tags_len++; + + for (tag = sd_device_get_tag_first(device); tag; tag = sd_device_get_tag_next(device)) { + char *e; + + if (!GREEDY_REALLOC(tags, tags_allocated, tags_len + strlen(tag) + 2)) + return -ENOMEM; + e = stpcpy(stpcpy(tags + tags_len, tag), ":"); + tags_len = e - tags; + } + + r = device_add_property_internal(device, "TAGS", tags); + if (r < 0) + return r; + + device->property_tags_outdated = false; + } + + return 0; +} + +_public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) { + const char *key; + const char *value; + int r; + + assert_return(device, NULL); + + r = device_properties_prepare(device); + if (r < 0) + return NULL; + + device->properties_iterator_generation = device->properties_generation; + device->properties_iterator = ITERATOR_FIRST; + + ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)&value, (const void**)&key); + + if (_value) + *_value = value; + + return key; +} + +_public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) { + const char *key; + const char *value; + int r; + + assert_return(device, NULL); + + r = device_properties_prepare(device); + if (r < 0) + return NULL; + + if (device->properties_iterator_generation != device->properties_generation) + return NULL; + + ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)&value, (const void**)&key); + + if (_value) + *_value = value; + + return key; +} + +static int device_sysattrs_read_all(sd_device *device) { + _cleanup_closedir_ DIR *dir = NULL; + const char *syspath; + struct dirent *dent; + int r; + + assert(device); + + if (device->sysattrs_read) + return 0; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + dir = opendir(syspath); + if (!dir) + return -errno; + + r = set_ensure_allocated(&device->sysattrs, &string_hash_ops); + if (r < 0) + return r; + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char *path; + struct stat statbuf; + + /* only handle symlinks and regular files */ + if (dent->d_type != DT_LNK && dent->d_type != DT_REG) + continue; + + path = strjoina(syspath, "/", dent->d_name); + + if (lstat(path, &statbuf) != 0) + continue; + + if (!(statbuf.st_mode & S_IRUSR)) + continue; + + r = set_put_strdup(device->sysattrs, dent->d_name); + if (r < 0) + return r; + } + + device->sysattrs_read = true; + + return 0; +} + +_public_ const char *sd_device_get_sysattr_first(sd_device *device) { + void *v; + int r; + + assert_return(device, NULL); + + if (!device->sysattrs_read) { + r = device_sysattrs_read_all(device); + if (r < 0) { + errno = -r; + return NULL; + } + } + + device->sysattrs_iterator = ITERATOR_FIRST; + + (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_sysattr_next(sd_device *device) { + void *v; + + assert_return(device, NULL); + + if (!device->sysattrs_read) + return NULL; + + (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); + return v; +} + +_public_ int sd_device_has_tag(sd_device *device, const char *tag) { + assert_return(device, -EINVAL); + assert_return(tag, -EINVAL); + + (void) device_read_db(device); + + return !!set_contains(device->tags, tag); +} + +_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) { + char *value; + int r; + + assert_return(device, -EINVAL); + assert_return(key, -EINVAL); + assert_return(_value, -EINVAL); + + r = device_properties_prepare(device); + if (r < 0) + return r; + + value = ordered_hashmap_get(device->properties, key); + if (!value) + return -ENOENT; + + *_value = value; + + return 0; +} + +/* replaces the value if it already exists */ +static int device_add_sysattr_value(sd_device *device, const char *_key, char *value) { + _cleanup_free_ char *key = NULL; + _cleanup_free_ char *value_old = NULL; + int r; + + assert(device); + assert(_key); + + r = hashmap_ensure_allocated(&device->sysattr_values, &string_hash_ops); + if (r < 0) + return r; + + value_old = hashmap_remove2(device->sysattr_values, _key, (void **)&key); + if (!key) { + key = strdup(_key); + if (!key) + return -ENOMEM; + } + + r = hashmap_put(device->sysattr_values, key, value); + if (r < 0) + return r; + + key = NULL; + + return 0; +} + +static int device_get_sysattr_value(sd_device *device, const char *_key, const char **_value) { + const char *key = NULL, *value; + + assert(device); + assert(_key); + + value = hashmap_get2(device->sysattr_values, _key, (void **) &key); + if (!key) + return -ENOENT; + + if (_value) + *_value = value; + + return 0; +} + +/* We cache all sysattr lookups. If an attribute does not exist, it is stored + * with a NULL value in the cache, otherwise the returned string is stored */ +_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) { + _cleanup_free_ char *value = NULL; + const char *syspath, *cached_value = NULL; + char *path; + struct stat statbuf; + int r; + + assert_return(device, -EINVAL); + assert_return(sysattr, -EINVAL); + + /* look for possibly already cached result */ + r = device_get_sysattr_value(device, sysattr, &cached_value); + if (r != -ENOENT) { + if (r < 0) + return r; + + if (!cached_value) + /* we looked up the sysattr before and it did not exist */ + return -ENOENT; + + if (_value) + *_value = cached_value; + + return 0; + } + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/", sysattr); + r = lstat(path, &statbuf); + if (r < 0) { + /* remember that we could not access the sysattr */ + r = device_add_sysattr_value(device, sysattr, NULL); + if (r < 0) + return r; + + return -ENOENT; + } else if (S_ISLNK(statbuf.st_mode)) { + /* Some core links return only the last element of the target path, + * these are just values, the paths should not be exposed. */ + if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) { + r = readlink_value(path, &value); + if (r < 0) + return r; + } else + return -EINVAL; + } else if (S_ISDIR(statbuf.st_mode)) { + /* skip directories */ + return -EINVAL; + } else if (!(statbuf.st_mode & S_IRUSR)) { + /* skip non-readable files */ + return -EPERM; + } else { + size_t size; + + /* read attribute value */ + r = read_full_file(path, &value, &size); + if (r < 0) + return r; + + /* drop trailing newlines */ + while (size > 0 && value[--size] == '\n') + value[size] = '\0'; + } + + r = device_add_sysattr_value(device, sysattr, value); + if (r < 0) + return r; + + *_value = value; + value = NULL; + + return 0; +} + +static void device_remove_sysattr_value(sd_device *device, const char *_key) { + _cleanup_free_ char *key = NULL; + _cleanup_free_ char *value = NULL; + + assert(device); + assert(_key); + + value = hashmap_remove2(device->sysattr_values, _key, (void **) &key); + + return; +} + +/* set the attribute and save it in the cache. If a NULL value is passed the + * attribute is cleared from the cache */ +_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *_value) { + _cleanup_close_ int fd = -1; + _cleanup_free_ char *value = NULL; + const char *syspath; + char *path; + struct stat statbuf; + size_t value_len = 0; + ssize_t size; + int r; + + assert_return(device, -EINVAL); + assert_return(sysattr, -EINVAL); + + if (!_value) { + device_remove_sysattr_value(device, sysattr); + + return 0; + } + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/", sysattr); + r = lstat(path, &statbuf); + if (r < 0) { + value = strdup(""); + if (!value) + return -ENOMEM; + + r = device_add_sysattr_value(device, sysattr, value); + if (r < 0) + return r; + + return -ENXIO; + } + + if (S_ISLNK(statbuf.st_mode)) + return -EINVAL; + + /* skip directories */ + if (S_ISDIR(statbuf.st_mode)) + return -EISDIR; + + /* skip non-readable files */ + if ((statbuf.st_mode & S_IRUSR) == 0) + return -EACCES; + + value_len = strlen(_value); + + /* drop trailing newlines */ + while (value_len > 0 && _value[value_len - 1] == '\n') + _value[--value_len] = '\0'; + + /* value length is limited to 4k */ + if (value_len > 4096) + return -EINVAL; + + fd = open(path, O_WRONLY | O_CLOEXEC); + if (fd < 0) + return -errno; + + value = strdup(_value); + if (!value) + return -ENOMEM; + + size = write(fd, value, value_len); + if (size < 0) + return -errno; + + if ((size_t)size != value_len) + return -EIO; + + r = device_add_sysattr_value(device, sysattr, value); + if (r < 0) + return r; + + value = NULL; + + return 0; +} diff --git a/src/libsystemd/src/sd-event/sd-event.c b/src/libsystemd/src/sd-event/sd-event.c new file mode 100644 index 0000000000..7f6f485353 --- /dev/null +++ b/src/libsystemd/src/sd-event/sd-event.c @@ -0,0 +1,2898 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include +#include +#include + +#include "alloc-util.h" +#include "fd-util.h" +#include "hashmap.h" +#include "list.h" +#include "macro.h" +#include "missing.h" +#include "prioq.h" +#include "process-util.h" +#include "set.h" +#include "signal-util.h" +#include "string-table.h" +#include "string-util.h" +#include "time-util.h" +#include "util.h" + +#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC) + +typedef enum EventSourceType { + SOURCE_IO, + SOURCE_TIME_REALTIME, + SOURCE_TIME_BOOTTIME, + SOURCE_TIME_MONOTONIC, + SOURCE_TIME_REALTIME_ALARM, + SOURCE_TIME_BOOTTIME_ALARM, + SOURCE_SIGNAL, + SOURCE_CHILD, + SOURCE_DEFER, + SOURCE_POST, + SOURCE_EXIT, + SOURCE_WATCHDOG, + _SOURCE_EVENT_SOURCE_TYPE_MAX, + _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1 +} EventSourceType; + +static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = { + [SOURCE_IO] = "io", + [SOURCE_TIME_REALTIME] = "realtime", + [SOURCE_TIME_BOOTTIME] = "bootime", + [SOURCE_TIME_MONOTONIC] = "monotonic", + [SOURCE_TIME_REALTIME_ALARM] = "realtime-alarm", + [SOURCE_TIME_BOOTTIME_ALARM] = "boottime-alarm", + [SOURCE_SIGNAL] = "signal", + [SOURCE_CHILD] = "child", + [SOURCE_DEFER] = "defer", + [SOURCE_POST] = "post", + [SOURCE_EXIT] = "exit", + [SOURCE_WATCHDOG] = "watchdog", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int); + +/* All objects we use in epoll events start with this value, so that + * we know how to dispatch it */ +typedef enum WakeupType { + WAKEUP_NONE, + WAKEUP_EVENT_SOURCE, + WAKEUP_CLOCK_DATA, + WAKEUP_SIGNAL_DATA, + _WAKEUP_TYPE_MAX, + _WAKEUP_TYPE_INVALID = -1, +} WakeupType; + +#define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM) + +struct sd_event_source { + WakeupType wakeup; + + unsigned n_ref; + + sd_event *event; + void *userdata; + sd_event_handler_t prepare; + + char *description; + + EventSourceType type:5; + int enabled:3; + bool pending:1; + bool dispatching:1; + bool floating:1; + + int64_t priority; + unsigned pending_index; + unsigned prepare_index; + unsigned pending_iteration; + unsigned prepare_iteration; + + LIST_FIELDS(sd_event_source, sources); + + 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; + } post; + struct { + sd_event_handler_t callback; + unsigned prioq_index; + } exit; + }; +}; + +struct clock_data { + WakeupType wakeup; + int fd; + + /* For all 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 *earliest; + Prioq *latest; + usec_t next; + + bool needs_rearm:1; +}; + +struct signal_data { + WakeupType wakeup; + + /* For each priority we maintain one signal fd, so that we + * only have to dequeue a single event per priority at a + * time. */ + + int fd; + int64_t priority; + sigset_t sigset; + sd_event_source *current; +}; + +struct sd_event { + unsigned n_ref; + + int epoll_fd; + int watchdog_fd; + + Prioq *pending; + Prioq *prepare; + + /* timerfd_create() only supports these five clocks so far. We + * can add support for more clocks when the kernel learns to + * deal with them, too. */ + struct clock_data realtime; + struct clock_data boottime; + struct clock_data monotonic; + struct clock_data realtime_alarm; + struct clock_data boottime_alarm; + + usec_t perturb; + + sd_event_source **signal_sources; /* indexed by signal number */ + Hashmap *signal_data; /* indexed by priority */ + + Hashmap *child_sources; + unsigned n_enabled_child_sources; + + Set *post_sources; + + Prioq *exit; + + pid_t original_pid; + + unsigned iteration; + dual_timestamp timestamp; + usec_t timestamp_boottime; + int state; + + bool exit_requested:1; + bool need_process_child:1; + bool watchdog:1; + bool profile_delays:1; + + int exit_code; + + pid_t tid; + sd_event **default_event_ptr; + + usec_t watchdog_last, watchdog_period; + + unsigned n_sources; + + LIST_HEAD(sd_event_source, sources); + + usec_t last_run, last_log; + unsigned delays[sizeof(usec_t) * 8]; +}; + +static void source_disconnect(sd_event_source *s); + +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; + + 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); + + /* 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 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; + + /* Lower priority values first */ + if (x->priority < y->priority) + return -1; + if (x->priority > y->priority) + 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(EVENT_SOURCE_IS_TIME(x->type)); + assert(x->type == y->type); + + /* 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; + + return 0; +} + +static usec_t time_event_source_latest(const sd_event_source *s) { + return usec_add(s->time.next, s->time.accuracy); +} + +static int latest_time_prioq_compare(const void *a, const void *b) { + const sd_event_source *x = a, *y = b; + + assert(EVENT_SOURCE_IS_TIME(x->type)); + assert(x->type == y->type); + + /* 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 (time_event_source_latest(x) < time_event_source_latest(y)) + return -1; + if (time_event_source_latest(x) > time_event_source_latest(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; + + return 0; +} + +static void free_clock_data(struct clock_data *d) { + assert(d); + assert(d->wakeup == WAKEUP_CLOCK_DATA); + + safe_close(d->fd); + prioq_free(d->earliest); + prioq_free(d->latest); +} + +static void event_free(sd_event *e) { + sd_event_source *s; + + assert(e); + + while ((s = e->sources)) { + assert(s->floating); + source_disconnect(s); + sd_event_source_unref(s); + } + + assert(e->n_sources == 0); + + if (e->default_event_ptr) + *(e->default_event_ptr) = NULL; + + safe_close(e->epoll_fd); + safe_close(e->watchdog_fd); + + free_clock_data(&e->realtime); + free_clock_data(&e->boottime); + free_clock_data(&e->monotonic); + free_clock_data(&e->realtime_alarm); + free_clock_data(&e->boottime_alarm); + + prioq_free(e->pending); + prioq_free(e->prepare); + prioq_free(e->exit); + + free(e->signal_sources); + hashmap_free(e->signal_data); + + hashmap_free(e->child_sources); + set_free(e->post_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->watchdog_fd = e->epoll_fd = e->realtime.fd = e->boottime.fd = e->monotonic.fd = e->realtime_alarm.fd = e->boottime_alarm.fd = -1; + e->realtime.next = e->boottime.next = e->monotonic.next = e->realtime_alarm.next = e->boottime_alarm.next = USEC_INFINITY; + e->realtime.wakeup = e->boottime.wakeup = e->monotonic.wakeup = e->realtime_alarm.wakeup = e->boottime_alarm.wakeup = WAKEUP_CLOCK_DATA; + e->original_pid = getpid(); + e->perturb = USEC_INFINITY; + + r = prioq_ensure_allocated(&e->pending, pending_prioq_compare); + if (r < 0) + goto fail; + + e->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (e->epoll_fd < 0) { + r = -errno; + goto fail; + } + + if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) { + log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s."); + e->profile_delays = true; + } + + *ret = e; + return 0; + +fail: + event_free(e); + return r; +} + +_public_ sd_event* sd_event_ref(sd_event *e) { + + if (!e) + return 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 an event loop and keeping + * it around over a fork(). Let's complain. */ + + return e->original_pid != getpid(); +} + +static void source_io_unregister(sd_event_source *s) { + int r; + + assert(s); + assert(s->type == SOURCE_IO); + + if (event_pid_changed(s->event)) + return; + + if (!s->io.registered) + return; + + r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL); + if (r < 0) + log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll: %m", + strna(s->description), event_source_type_to_string(s->type)); + + s->io.registered = false; +} + +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 clockid_t event_source_type_to_clock(EventSourceType t) { + + switch (t) { + + case SOURCE_TIME_REALTIME: + return CLOCK_REALTIME; + + case SOURCE_TIME_BOOTTIME: + return CLOCK_BOOTTIME; + + case SOURCE_TIME_MONOTONIC: + return CLOCK_MONOTONIC; + + case SOURCE_TIME_REALTIME_ALARM: + return CLOCK_REALTIME_ALARM; + + case SOURCE_TIME_BOOTTIME_ALARM: + return CLOCK_BOOTTIME_ALARM; + + default: + return (clockid_t) -1; + } +} + +static EventSourceType clock_to_event_source_type(clockid_t clock) { + + switch (clock) { + + case CLOCK_REALTIME: + return SOURCE_TIME_REALTIME; + + case CLOCK_BOOTTIME: + return SOURCE_TIME_BOOTTIME; + + case CLOCK_MONOTONIC: + return SOURCE_TIME_MONOTONIC; + + case CLOCK_REALTIME_ALARM: + return SOURCE_TIME_REALTIME_ALARM; + + case CLOCK_BOOTTIME_ALARM: + return SOURCE_TIME_BOOTTIME_ALARM; + + default: + return _SOURCE_EVENT_SOURCE_TYPE_INVALID; + } +} + +static struct clock_data* event_get_clock_data(sd_event *e, EventSourceType t) { + assert(e); + + switch (t) { + + case SOURCE_TIME_REALTIME: + return &e->realtime; + + case SOURCE_TIME_BOOTTIME: + return &e->boottime; + + case SOURCE_TIME_MONOTONIC: + return &e->monotonic; + + case SOURCE_TIME_REALTIME_ALARM: + return &e->realtime_alarm; + + case SOURCE_TIME_BOOTTIME_ALARM: + return &e->boottime_alarm; + + default: + return NULL; + } +} + +static int event_make_signal_data( + sd_event *e, + int sig, + struct signal_data **ret) { + + struct epoll_event ev = {}; + struct signal_data *d; + bool added = false; + sigset_t ss_copy; + int64_t priority; + int r; + + assert(e); + + if (event_pid_changed(e)) + return -ECHILD; + + if (e->signal_sources && e->signal_sources[sig]) + priority = e->signal_sources[sig]->priority; + else + priority = 0; + + d = hashmap_get(e->signal_data, &priority); + if (d) { + if (sigismember(&d->sigset, sig) > 0) { + if (ret) + *ret = d; + return 0; + } + } else { + r = hashmap_ensure_allocated(&e->signal_data, &uint64_hash_ops); + if (r < 0) + return r; + + d = new0(struct signal_data, 1); + if (!d) + return -ENOMEM; + + d->wakeup = WAKEUP_SIGNAL_DATA; + d->fd = -1; + d->priority = priority; + + r = hashmap_put(e->signal_data, &d->priority, d); + if (r < 0) { + free(d); + return r; + } + + added = true; + } + + ss_copy = d->sigset; + assert_se(sigaddset(&ss_copy, sig) >= 0); + + r = signalfd(d->fd, &ss_copy, SFD_NONBLOCK|SFD_CLOEXEC); + if (r < 0) { + r = -errno; + goto fail; + } + + d->sigset = ss_copy; + + if (d->fd >= 0) { + if (ret) + *ret = d; + return 0; + } + + d->fd = r; + + ev.events = EPOLLIN; + ev.data.ptr = d; + + r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, d->fd, &ev); + if (r < 0) { + r = -errno; + goto fail; + } + + if (ret) + *ret = d; + + return 0; + +fail: + if (added) { + d->fd = safe_close(d->fd); + hashmap_remove(e->signal_data, &d->priority); + free(d); + } + + return r; +} + +static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig) { + assert(e); + assert(d); + + /* Turns off the specified signal in the signal data + * object. If the signal mask of the object becomes empty that + * way removes it. */ + + if (sigismember(&d->sigset, sig) == 0) + return; + + assert_se(sigdelset(&d->sigset, sig) >= 0); + + if (sigisemptyset(&d->sigset)) { + + /* If all the mask is all-zero we can get rid of the structure */ + hashmap_remove(e->signal_data, &d->priority); + assert(!d->current); + safe_close(d->fd); + free(d); + return; + } + + assert(d->fd >= 0); + + if (signalfd(d->fd, &d->sigset, SFD_NONBLOCK|SFD_CLOEXEC) < 0) + log_debug_errno(errno, "Failed to unset signal bit, ignoring: %m"); +} + +static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig) { + struct signal_data *d; + static const int64_t zero_priority = 0; + + assert(e); + + /* Rechecks if the specified signal is still something we are + * interested in. If not, we'll unmask it, and possibly drop + * the signalfd for it. */ + + if (sig == SIGCHLD && + e->n_enabled_child_sources > 0) + return; + + if (e->signal_sources && + e->signal_sources[sig] && + e->signal_sources[sig]->enabled != SD_EVENT_OFF) + return; + + /* + * The specified signal might be enabled in three different queues: + * + * 1) the one that belongs to the priority passed (if it is non-NULL) + * 2) the one that belongs to the priority of the event source of the signal (if there is one) + * 3) the 0 priority (to cover the SIGCHLD case) + * + * Hence, let's remove it from all three here. + */ + + if (priority) { + d = hashmap_get(e->signal_data, priority); + if (d) + event_unmask_signal_data(e, d, sig); + } + + if (e->signal_sources && e->signal_sources[sig]) { + d = hashmap_get(e->signal_data, &e->signal_sources[sig]->priority); + if (d) + event_unmask_signal_data(e, d, sig); + } + + d = hashmap_get(e->signal_data, &zero_priority); + if (d) + event_unmask_signal_data(e, d, sig); +} + +static void source_disconnect(sd_event_source *s) { + sd_event *event; + + assert(s); + + if (!s->event) + return; + + assert(s->event->n_sources > 0); + + switch (s->type) { + + case SOURCE_IO: + if (s->io.fd >= 0) + source_io_unregister(s); + + break; + + case SOURCE_TIME_REALTIME: + case SOURCE_TIME_BOOTTIME: + case SOURCE_TIME_MONOTONIC: + case SOURCE_TIME_REALTIME_ALARM: + case SOURCE_TIME_BOOTTIME_ALARM: { + struct clock_data *d; + + d = event_get_clock_data(s->event, s->type); + assert(d); + + prioq_remove(d->earliest, s, &s->time.earliest_index); + prioq_remove(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + break; + } + + case SOURCE_SIGNAL: + if (s->signal.sig > 0) { + + if (s->event->signal_sources) + s->event->signal_sources[s->signal.sig] = NULL; + + event_gc_signal_data(s->event, &s->priority, s->signal.sig); + } + + 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--; + } + + (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid)); + event_gc_signal_data(s->event, &s->priority, SIGCHLD); + } + + break; + + case SOURCE_DEFER: + /* nothing */ + break; + + case SOURCE_POST: + set_remove(s->event->post_sources, s); + break; + + case SOURCE_EXIT: + prioq_remove(s->event->exit, s, &s->exit.prioq_index); + break; + + default: + 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); + + event = s->event; + + s->type = _SOURCE_EVENT_SOURCE_TYPE_INVALID; + s->event = NULL; + LIST_REMOVE(sources, event->sources, s); + event->n_sources--; + + if (!s->floating) + sd_event_unref(event); +} + +static void source_free(sd_event_source *s) { + assert(s); + + source_disconnect(s); + free(s->description); + 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 (EVENT_SOURCE_IS_TIME(s->type)) { + struct clock_data *d; + + d = event_get_clock_data(s->event, s->type); + assert(d); + + prioq_reshuffle(d->earliest, s, &s->time.earliest_index); + prioq_reshuffle(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + } + + if (s->type == SOURCE_SIGNAL && !b) { + struct signal_data *d; + + d = hashmap_get(s->event->signal_data, &s->priority); + if (d && d->current == s) + d->current = NULL; + } + + return 0; +} + +static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType type) { + sd_event_source *s; + + assert(e); + + s = new0(sd_event_source, 1); + if (!s) + return NULL; + + s->n_ref = 1; + s->event = e; + s->floating = floating; + s->type = type; + s->pending_index = s->prepare_index = PRIOQ_IDX_NULL; + + if (!floating) + sd_event_ref(e); + + LIST_PREPEND(sources, e->sources, s); + e->n_sources++; + + return s; +} + +_public_ int sd_event_add_io( + sd_event *e, + sd_event_source **ret, + int fd, + uint32_t events, + sd_event_io_handler_t callback, + void *userdata) { + + sd_event_source *s; + int r; + + assert_return(e, -EINVAL); + assert_return(fd >= 0, -EBADF); + assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL); + assert_return(callback, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + s = source_new(e, !ret, SOURCE_IO); + if (!s) + return -ENOMEM; + + s->wakeup = WAKEUP_EVENT_SOURCE; + 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 r; + } + + if (ret) + *ret = s; + + return 0; +} + +static void initialize_perturb(sd_event *e) { + sd_id128_t bootid = {}; + + /* 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 (_likely_(e->perturb != USEC_INFINITY)) + return; + + if (sd_id128_get_boot(&bootid) >= 0) + e->perturb = (bootid.qwords[0] ^ bootid.qwords[1]) % USEC_PER_MINUTE; +} + +static int event_setup_timer_fd( + sd_event *e, + struct clock_data *d, + clockid_t clock) { + + struct epoll_event ev = {}; + int r, fd; + + assert(e); + assert(d); + + if (_likely_(d->fd >= 0)) + return 0; + + fd = timerfd_create(clock, TFD_NONBLOCK|TFD_CLOEXEC); + if (fd < 0) + return -errno; + + ev.events = EPOLLIN; + ev.data.ptr = d; + + r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, fd, &ev); + if (r < 0) { + safe_close(fd); + return -errno; + } + + d->fd = fd; + return 0; +} + +static int time_exit_callback(sd_event_source *s, uint64_t usec, void *userdata) { + assert(s); + + return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata)); +} + +_public_ int sd_event_add_time( + sd_event *e, + sd_event_source **ret, + clockid_t clock, + uint64_t usec, + uint64_t accuracy, + sd_event_time_handler_t callback, + void *userdata) { + + EventSourceType type; + sd_event_source *s; + struct clock_data *d; + int r; + + assert_return(e, -EINVAL); + assert_return(accuracy != (uint64_t) -1, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && + !clock_boottime_supported()) + return -EOPNOTSUPP; + + if (!callback) + callback = time_exit_callback; + + type = clock_to_event_source_type(clock); + assert_return(type >= 0, -EOPNOTSUPP); + + d = event_get_clock_data(e, type); + assert(d); + + r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare); + if (r < 0) + return r; + + r = prioq_ensure_allocated(&d->latest, latest_time_prioq_compare); + if (r < 0) + return r; + + if (d->fd < 0) { + r = event_setup_timer_fd(e, d, clock); + if (r < 0) + return r; + } + + s = source_new(e, !ret, 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; + + d->needs_rearm = true; + + r = prioq_put(d->earliest, s, &s->time.earliest_index); + if (r < 0) + goto fail; + + r = prioq_put(d->latest, s, &s->time.latest_index); + if (r < 0) + goto fail; + + if (ret) + *ret = s; + + return 0; + +fail: + source_free(s); + return r; +} + +static int signal_exit_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + assert(s); + + return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata)); +} + +_public_ int sd_event_add_signal( + sd_event *e, + sd_event_source **ret, + int sig, + sd_event_signal_handler_t callback, + void *userdata) { + + sd_event_source *s; + struct signal_data *d; + sigset_t ss; + int r; + + assert_return(e, -EINVAL); + assert_return(SIGNAL_VALID(sig), -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + if (!callback) + callback = signal_exit_callback; + + r = pthread_sigmask(SIG_SETMASK, NULL, &ss); + if (r != 0) + return -r; + + 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, !ret, 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; + + r = event_make_signal_data(e, sig, &d); + if (r < 0) { + source_free(s); + return r; + } + + /* Use the signal name as description for the event source by default */ + (void) sd_event_source_set_description(s, signal_to_string(sig)); + + if (ret) + *ret = s; + + return 0; +} + +_public_ int sd_event_add_child( + sd_event *e, + sd_event_source **ret, + pid_t pid, + int options, + sd_event_child_handler_t callback, + void *userdata) { + + 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(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + r = hashmap_ensure_allocated(&e->child_sources, NULL); + if (r < 0) + return r; + + if (hashmap_contains(e->child_sources, PID_TO_PTR(pid))) + return -EBUSY; + + s = source_new(e, !ret, 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, PID_TO_PTR(pid), s); + if (r < 0) { + source_free(s); + return r; + } + + e->n_enabled_child_sources++; + + r = event_make_signal_data(e, SIGCHLD, NULL); + if (r < 0) { + e->n_enabled_child_sources--; + source_free(s); + return r; + } + + e->need_process_child = true; + + if (ret) + *ret = s; + + return 0; +} + +_public_ int sd_event_add_defer( + sd_event *e, + sd_event_source **ret, + sd_event_handler_t callback, + void *userdata) { + + sd_event_source *s; + int r; + + assert_return(e, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + s = source_new(e, !ret, 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; + } + + if (ret) + *ret = s; + + return 0; +} + +_public_ int sd_event_add_post( + sd_event *e, + sd_event_source **ret, + sd_event_handler_t callback, + void *userdata) { + + sd_event_source *s; + int r; + + assert_return(e, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + r = set_ensure_allocated(&e->post_sources, NULL); + if (r < 0) + return r; + + s = source_new(e, !ret, SOURCE_POST); + if (!s) + return -ENOMEM; + + s->post.callback = callback; + s->userdata = userdata; + s->enabled = SD_EVENT_ON; + + r = set_put(e->post_sources, s); + if (r < 0) { + source_free(s); + return r; + } + + if (ret) + *ret = s; + + return 0; +} + +_public_ int sd_event_add_exit( + sd_event *e, + sd_event_source **ret, + sd_event_handler_t callback, + void *userdata) { + + sd_event_source *s; + int r; + + assert_return(e, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + r = prioq_ensure_allocated(&e->exit, exit_prioq_compare); + if (r < 0) + return r; + + s = source_new(e, !ret, 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; + } + + if (ret) + *ret = s; + + return 0; +} + +_public_ sd_event_source* sd_event_source_ref(sd_event_source *s) { + + if (!s) + return 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); + + source_disconnect(s); + } else + source_free(s); + } + + return NULL; +} + +_public_ int sd_event_source_set_description(sd_event_source *s, const char *description) { + assert_return(s, -EINVAL); + assert_return(!event_pid_changed(s->event), -ECHILD); + + return free_and_strdup(&s->description, description); +} + +_public_ int sd_event_source_get_description(sd_event_source *s, const char **description) { + assert_return(s, -EINVAL); + assert_return(description, -EINVAL); + assert_return(s->description, -ENXIO); + assert_return(!event_pid_changed(s->event), -ECHILD); + + *description = s->description; + return 0; +} + +_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, -EBADF); + 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); + + /* edge-triggered updates are never skipped, so we can reset edges */ + if (s->io.events == events && !(events & EPOLLET)) + 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) { + int r; + + 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; + + if (s->type == SOURCE_SIGNAL && s->enabled != SD_EVENT_OFF) { + struct signal_data *old, *d; + + /* Move us from the signalfd belonging to the old + * priority to the signalfd of the new priority */ + + assert_se(old = hashmap_get(s->event->signal_data, &s->priority)); + + s->priority = priority; + + r = event_make_signal_data(s->event, s->signal.sig, &d); + if (r < 0) { + s->priority = old->priority; + return r; + } + + event_unmask_signal_data(s->event, old, s->signal.sig); + } else + 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(!event_pid_changed(s->event), -ECHILD); + + /* If we are dead anyway, we are fine with turning off + * sources, but everything else needs to fail. */ + if (s->event->state == SD_EVENT_FINISHED) + return m == SD_EVENT_OFF ? 0 : -ESTALE; + + if (s->enabled == m) + return 0; + + if (m == SD_EVENT_OFF) { + + switch (s->type) { + + case SOURCE_IO: + source_io_unregister(s); + s->enabled = m; + break; + + case SOURCE_TIME_REALTIME: + case SOURCE_TIME_BOOTTIME: + case SOURCE_TIME_MONOTONIC: + case SOURCE_TIME_REALTIME_ALARM: + case SOURCE_TIME_BOOTTIME_ALARM: { + struct clock_data *d; + + s->enabled = m; + d = event_get_clock_data(s->event, s->type); + assert(d); + + prioq_reshuffle(d->earliest, s, &s->time.earliest_index); + prioq_reshuffle(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + break; + } + + case SOURCE_SIGNAL: + s->enabled = m; + + event_gc_signal_data(s->event, &s->priority, s->signal.sig); + break; + + case SOURCE_CHILD: + s->enabled = m; + + assert(s->event->n_enabled_child_sources > 0); + s->event->n_enabled_child_sources--; + + event_gc_signal_data(s->event, &s->priority, SIGCHLD); + break; + + case SOURCE_EXIT: + s->enabled = m; + prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); + break; + + case SOURCE_DEFER: + case SOURCE_POST: + s->enabled = m; + break; + + default: + 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_TIME_REALTIME: + case SOURCE_TIME_BOOTTIME: + case SOURCE_TIME_MONOTONIC: + case SOURCE_TIME_REALTIME_ALARM: + case SOURCE_TIME_BOOTTIME_ALARM: { + struct clock_data *d; + + s->enabled = m; + d = event_get_clock_data(s->event, s->type); + assert(d); + + prioq_reshuffle(d->earliest, s, &s->time.earliest_index); + prioq_reshuffle(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + break; + } + + case SOURCE_SIGNAL: + + s->enabled = m; + + r = event_make_signal_data(s->event, s->signal.sig, NULL); + if (r < 0) { + s->enabled = SD_EVENT_OFF; + event_gc_signal_data(s->event, &s->priority, s->signal.sig); + return r; + } + + break; + + case SOURCE_CHILD: + + if (s->enabled == SD_EVENT_OFF) + s->event->n_enabled_child_sources++; + + s->enabled = m; + + r = event_make_signal_data(s->event, SIGCHLD, NULL); + if (r < 0) { + s->enabled = SD_EVENT_OFF; + s->event->n_enabled_child_sources--; + event_gc_signal_data(s->event, &s->priority, SIGCHLD); + return r; + } + + break; + + case SOURCE_EXIT: + s->enabled = m; + prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); + break; + + case SOURCE_DEFER: + case SOURCE_POST: + s->enabled = m; + break; + + default: + 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(EVENT_SOURCE_IS_TIME(s->type), -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) { + struct clock_data *d; + + assert_return(s, -EINVAL); + assert_return(EVENT_SOURCE_IS_TIME(s->type), -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); + + d = event_get_clock_data(s->event, s->type); + assert(d); + + prioq_reshuffle(d->earliest, s, &s->time.earliest_index); + prioq_reshuffle(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + + 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(EVENT_SOURCE_IS_TIME(s->type), -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) { + struct clock_data *d; + + assert_return(s, -EINVAL); + assert_return(usec != (uint64_t) -1, -EINVAL); + assert_return(EVENT_SOURCE_IS_TIME(s->type), -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); + + d = event_get_clock_data(s->event, s->type); + assert(d); + + prioq_reshuffle(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + + return 0; +} + +_public_ int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock) { + assert_return(s, -EINVAL); + assert_return(clock, -EINVAL); + assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); + assert_return(!event_pid_changed(s->event), -ECHILD); + + *clock = event_source_type_to_clock(s->type); + 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 (a >= USEC_INFINITY) + return USEC_INFINITY; + + if (b <= a + 1) + return a; + + initialize_perturb(e); + + /* + 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, + struct clock_data *d) { + + struct itimerspec its = {}; + sd_event_source *a, *b; + usec_t t; + int r; + + assert(e); + assert(d); + + if (!d->needs_rearm) + return 0; + else + d->needs_rearm = false; + + a = prioq_peek(d->earliest); + if (!a || a->enabled == SD_EVENT_OFF || a->time.next == USEC_INFINITY) { + + if (d->fd < 0) + return 0; + + if (d->next == USEC_INFINITY) + return 0; + + /* disarm */ + r = timerfd_settime(d->fd, TFD_TIMER_ABSTIME, &its, NULL); + if (r < 0) + return r; + + d->next = USEC_INFINITY; + return 0; + } + + b = prioq_peek(d->latest); + assert_se(b && b->enabled != SD_EVENT_OFF); + + t = sleep_between(e, a->time.next, time_event_source_latest(b)); + if (d->next == t) + return 0; + + assert_se(d->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(d->fd, TFD_TIMER_ABSTIME, &its, NULL); + if (r < 0) + return -errno; + + d->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_INFINITY; + + return 0; +} + +static int process_timer( + sd_event *e, + usec_t n, + struct clock_data *d) { + + sd_event_source *s; + int r; + + assert(e); + assert(d); + + for (;;) { + s = prioq_peek(d->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(d->earliest, s, &s->time.earliest_index); + prioq_reshuffle(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + } + + 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, struct signal_data *d, uint32_t events) { + bool read_one = false; + int r; + + assert(e); + assert_return(events == EPOLLIN, -EIO); + + /* If there's a signal queued on this priority and SIGCHLD is + on this priority too, then make sure to recheck the + children we watch. This is because we only ever dequeue + the first signal per priority, and if we dequeue one, and + SIGCHLD might be enqueued later we wouldn't know, but we + might have higher priority children we care about hence we + need to check that explicitly. */ + + if (sigismember(&d->sigset, SIGCHLD)) + e->need_process_child = true; + + /* If there's already an event source pending for this + * priority we don't read another */ + if (d->current) + return 0; + + for (;;) { + struct signalfd_siginfo si; + ssize_t n; + sd_event_source *s = NULL; + + n = read(d->fd, &si, sizeof(si)); + if (n < 0) { + if (errno == EAGAIN || errno == EINTR) + return read_one; + + return -errno; + } + + if (_unlikely_(n != sizeof(si))) + return -EIO; + + assert(SIGNAL_VALID(si.ssi_signo)); + + read_one = true; + + if (e->signal_sources) + s = e->signal_sources[si.ssi_signo]; + if (!s) + continue; + if (s->pending) + continue; + + s->signal.siginfo = si; + d->current = s; + + r = source_set_pending(s, true); + if (r < 0) + return r; + + return 1; + } +} + +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->type != SOURCE_POST) { + sd_event_source *z; + Iterator i; + + /* If we execute a non-post source, let's mark all + * post sources as pending */ + + SET_FOREACH(z, s->event->post_sources, i) { + if (z->enabled == SD_EVENT_OFF) + continue; + + r = source_set_pending(z, true); + 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_TIME_REALTIME: + case SOURCE_TIME_BOOTTIME: + case SOURCE_TIME_MONOTONIC: + case SOURCE_TIME_REALTIME_ALARM: + case SOURCE_TIME_BOOTTIME_ALARM: + 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_POST: + r = s->post.callback(s, s->userdata); + break; + + case SOURCE_EXIT: + r = s->exit.callback(s, s->userdata); + break; + + case SOURCE_WATCHDOG: + case _SOURCE_EVENT_SOURCE_TYPE_MAX: + case _SOURCE_EVENT_SOURCE_TYPE_INVALID: + assert_not_reached("Wut? I shouldn't exist."); + } + + s->dispatching = false; + + if (r < 0) + log_debug_errno(r, "Event source %s (type %s) returned error, disabling: %m", + strna(s->description), event_source_type_to_string(s->type)); + + 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_errno(r, "Prepare callback of event source %s (type %s) returned error, disabling: %m", + strna(s->description), event_source_type_to_string(s->type)); + + 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_INITIAL; + 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); + + /* Make sure we never set the watchdog to 0, which tells the + * kernel to disable it. */ + if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0) + its.it_value.tv_nsec = 1; + + 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_prepare(sd_event *e) { + int r; + + 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_INITIAL, -EBUSY); + + if (e->exit_requested) + goto pending; + + e->iteration++; + + e->state = SD_EVENT_PREPARING; + r = event_prepare(e); + e->state = SD_EVENT_INITIAL; + if (r < 0) + return r; + + r = event_arm_timer(e, &e->realtime); + if (r < 0) + return r; + + r = event_arm_timer(e, &e->boottime); + if (r < 0) + return r; + + r = event_arm_timer(e, &e->monotonic); + if (r < 0) + return r; + + r = event_arm_timer(e, &e->realtime_alarm); + if (r < 0) + return r; + + r = event_arm_timer(e, &e->boottime_alarm); + if (r < 0) + return r; + + if (event_next_pending(e) || e->need_process_child) + goto pending; + + e->state = SD_EVENT_ARMED; + + return 0; + +pending: + e->state = SD_EVENT_ARMED; + r = sd_event_wait(e, 0); + if (r == 0) + e->state = SD_EVENT_ARMED; + + return r; +} + +_public_ int sd_event_wait(sd_event *e, uint64_t timeout) { + struct epoll_event *ev_queue; + unsigned ev_queue_max; + int r, m, i; + + 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_ARMED, -EBUSY); + + if (e->exit_requested) { + e->state = SD_EVENT_PENDING; + return 1; + } + + ev_queue_max = MAX(e->n_sources, 1u); + 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) { + if (errno == EINTR) { + e->state = SD_EVENT_PENDING; + return 1; + } + + r = -errno; + goto finish; + } + + dual_timestamp_get(&e->timestamp); + if (clock_boottime_supported()) + e->timestamp_boottime = now(CLOCK_BOOTTIME); + + for (i = 0; i < m; i++) { + + if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG)) + r = flush_timer(e, e->watchdog_fd, ev_queue[i].events, NULL); + else { + WakeupType *t = ev_queue[i].data.ptr; + + switch (*t) { + + case WAKEUP_EVENT_SOURCE: + r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events); + break; + + case WAKEUP_CLOCK_DATA: { + struct clock_data *d = ev_queue[i].data.ptr; + r = flush_timer(e, d->fd, ev_queue[i].events, &d->next); + break; + } + + case WAKEUP_SIGNAL_DATA: + r = process_signal(e, ev_queue[i].data.ptr, ev_queue[i].events); + break; + + default: + assert_not_reached("Invalid wake-up pointer"); + } + } + if (r < 0) + goto finish; + } + + r = process_watchdog(e); + if (r < 0) + goto finish; + + r = process_timer(e, e->timestamp.realtime, &e->realtime); + if (r < 0) + goto finish; + + r = process_timer(e, e->timestamp_boottime, &e->boottime); + if (r < 0) + goto finish; + + r = process_timer(e, e->timestamp.monotonic, &e->monotonic); + if (r < 0) + goto finish; + + r = process_timer(e, e->timestamp.realtime, &e->realtime_alarm); + if (r < 0) + goto finish; + + r = process_timer(e, e->timestamp_boottime, &e->boottime_alarm); + if (r < 0) + goto finish; + + if (e->need_process_child) { + r = process_child(e); + if (r < 0) + goto finish; + } + + if (event_next_pending(e)) { + e->state = SD_EVENT_PENDING; + + return 1; + } + + r = 0; + +finish: + e->state = SD_EVENT_INITIAL; + + return r; +} + +_public_ int sd_event_dispatch(sd_event *e) { + sd_event_source *p; + int r; + + 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_PENDING, -EBUSY); + + if (e->exit_requested) + return dispatch_exit(e); + + p = event_next_pending(e); + if (p) { + sd_event_ref(e); + + e->state = SD_EVENT_RUNNING; + r = source_dispatch(p); + e->state = SD_EVENT_INITIAL; + + sd_event_unref(e); + + return r; + } + + e->state = SD_EVENT_INITIAL; + + return 1; +} + +static void event_log_delays(sd_event *e) { + char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1]; + unsigned i; + int o; + + for (i = o = 0; i < ELEMENTSOF(e->delays); i++) { + o += snprintf(&b[o], sizeof(b) - o, "%u ", e->delays[i]); + e->delays[i] = 0; + } + log_debug("Event loop iterations: %.*s", o, b); +} + +_public_ int sd_event_run(sd_event *e, uint64_t timeout) { + int r; + + 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_INITIAL, -EBUSY); + + if (e->profile_delays && e->last_run) { + usec_t this_run; + unsigned l; + + this_run = now(CLOCK_MONOTONIC); + + l = u64log2(this_run - e->last_run); + assert(l < sizeof(e->delays)); + e->delays[l]++; + + if (this_run - e->last_log >= 5*USEC_PER_SEC) { + event_log_delays(e); + e->last_log = this_run; + } + } + + r = sd_event_prepare(e); + if (r == 0) + /* There was nothing? Then wait... */ + r = sd_event_wait(e, timeout); + + if (e->profile_delays) + e->last_run = now(CLOCK_MONOTONIC); + + if (r > 0) { + /* There's something now, then let's dispatch it */ + r = sd_event_dispatch(e); + if (r < 0) + return r; + + return 1; + } + + 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_INITIAL, -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_fd(sd_event *e) { + + assert_return(e, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + + return e->epoll_fd; +} + +_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_now(sd_event *e, clockid_t clock, uint64_t *usec) { + assert_return(e, -EINVAL); + assert_return(usec, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + assert_return(IN_SET(clock, + CLOCK_REALTIME, + CLOCK_REALTIME_ALARM, + CLOCK_MONOTONIC, + CLOCK_BOOTTIME, + CLOCK_BOOTTIME_ALARM), -EOPNOTSUPP); + + if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported()) + return -EOPNOTSUPP; + + if (!dual_timestamp_is_set(&e->timestamp)) { + /* Implicitly fall back to now() if we never ran + * before and thus have no cached time. */ + *usec = now(clock); + return 1; + } + + switch (clock) { + + case CLOCK_REALTIME: + case CLOCK_REALTIME_ALARM: + *usec = e->timestamp.realtime; + break; + + case CLOCK_MONOTONIC: + *usec = e->timestamp.monotonic; + break; + + case CLOCK_BOOTTIME: + case CLOCK_BOOTTIME_ALARM: + *usec = e->timestamp_boottime; + break; + + default: + assert_not_reached("Unknown clock?"); + } + + return 0; +} + +_public_ int sd_event_default(sd_event **ret) { + + static thread_local sd_event *default_event = NULL; + sd_event *e = NULL; + 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); + e->watchdog_fd = safe_close(e->watchdog_fd); + } + } + + e->watchdog = !!b; + return e->watchdog; + +fail: + e->watchdog_fd = safe_close(e->watchdog_fd); + 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/src/sd-event/test-event.c b/src/libsystemd/src/sd-event/test-event.c new file mode 100644 index 0000000000..324ad7ee02 --- /dev/null +++ b/src/libsystemd/src/sd-event/test-event.c @@ -0,0 +1,361 @@ +/*** + 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 . +***/ + +#include + +#include "fd-util.h" +#include "log.h" +#include "macro.h" +#include "signal-util.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_se(s); + assert_se(si); + + log_info("got child on %c", PTR_TO_INT(userdata)); + + assert_se(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 = NULL; + pid_t pid; + + assert_se(s); + assert_se(si); + + log_info("got signal on %c", PTR_TO_INT(userdata)); + + assert_se(userdata == INT_TO_PTR('e')); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); + + pid = fork(); + assert_se(pid >= 0); + + if (pid == 0) + _exit(0); + + assert_se(sd_event_add_child(sd_event_source_get_event(s), &p, pid, WEXITED, child_handler, INT_TO_PTR('f')) >= 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 = NULL; + + assert_se(s); + + log_info("got defer on %c", PTR_TO_INT(userdata)); + + assert_se(userdata == INT_TO_PTR('d')); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGUSR1, -1) >= 0); + + assert_se(sd_event_add_signal(sd_event_source_get_event(s), &p, SIGUSR1, signal_handler, INT_TO_PTR('e')) >= 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), &p, defer_handler, INT_TO_PTR('d')) >= 0); + assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0); + } else { + assert_se(!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; +} + +static bool got_post = false; + +static int post_handler(sd_event_source *s, void *userdata) { + log_info("got post handler"); + + got_post = true; + + return 2; +} + +static void test_basic(void) { + 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 }; + uint64_t event_now; + + 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_now(e, CLOCK_MONOTONIC, &event_now) > 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, &t, k[0], EPOLLIN, unref_handler, NULL) >= 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, &w, d[0], EPOLLIN, io_handler, INT_TO_PTR('d')) >= 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, &x, a[0], EPOLLIN, io_handler, INT_TO_PTR('a')) >= 0); + assert_se(sd_event_add_io(e, &y, b[0], EPOLLIN, io_handler, INT_TO_PTR('b')) >= 0); + assert_se(sd_event_add_time(e, &z, CLOCK_MONOTONIC, 0, 0, time_handler, INT_TO_PTR('c')) >= 0); + assert_se(sd_event_add_exit(e, &q, exit_handler, INT_TO_PTR('g')) >= 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); + + /* Test for floating event sources */ + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+1, -1) >= 0); + assert_se(sd_event_add_signal(e, NULL, SIGRTMIN+1, NULL, NULL) >= 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_add_post(e, NULL, post_handler, NULL) >= 0); + assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0); + assert_se(sd_event_source_set_time(z, event_now + 200 * USEC_PER_MSEC) >= 0); + assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0); + + assert_se(sd_event_loop(e) >= 0); + assert_se(got_post); + assert_se(got_exit); + + sd_event_source_unref(z); + sd_event_source_unref(q); + + sd_event_source_unref(w); + + sd_event_unref(e); + + safe_close_pair(a); + safe_close_pair(b); + safe_close_pair(d); + safe_close_pair(k); +} + +static void test_sd_event_now(void) { + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + uint64_t event_now; + + assert_se(sd_event_new(&e) >= 0); + assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0); + assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) > 0); + assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) > 0); + if (clock_boottime_supported()) { + assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0); + assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0); + } + assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP); + assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP); + + assert_se(sd_event_run(e, 0) == 0); + + assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0); + assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) == 0); + assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) == 0); + if (clock_boottime_supported()) { + assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0); + assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0); + } + assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP); + assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP); +} + +static int last_rtqueue_sigval = 0; +static int n_rtqueue = 0; + +static int rtqueue_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + last_rtqueue_sigval = si->ssi_int; + n_rtqueue++; + return 0; +} + +static void test_rtqueue(void) { + sd_event_source *u = NULL, *v = NULL, *s = NULL; + sd_event *e = NULL; + + assert_se(sd_event_default(&e) >= 0); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+2, SIGRTMIN+3, SIGUSR2, -1) >= 0); + assert_se(sd_event_add_signal(e, &u, SIGRTMIN+2, rtqueue_handler, NULL) >= 0); + assert_se(sd_event_add_signal(e, &v, SIGRTMIN+3, rtqueue_handler, NULL) >= 0); + assert_se(sd_event_add_signal(e, &s, SIGUSR2, rtqueue_handler, NULL) >= 0); + + assert_se(sd_event_source_set_priority(v, -10) >= 0); + + assert(sigqueue(getpid(), SIGRTMIN+2, (union sigval) { .sival_int = 1 }) >= 0); + assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 2 }) >= 0); + assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 3 }) >= 0); + assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 4 }) >= 0); + assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 5 }) >= 0); + + assert_se(n_rtqueue == 0); + assert_se(last_rtqueue_sigval == 0); + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(n_rtqueue == 1); + assert_se(last_rtqueue_sigval == 2); /* first SIGRTMIN+3 */ + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(n_rtqueue == 2); + assert_se(last_rtqueue_sigval == 4); /* second SIGRTMIN+3 */ + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(n_rtqueue == 3); + assert_se(last_rtqueue_sigval == 3); /* first SIGUSR2 */ + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(n_rtqueue == 4); + assert_se(last_rtqueue_sigval == 1); /* SIGRTMIN+2 */ + + assert_se(sd_event_run(e, 0) == 0); /* the other SIGUSR2 is dropped, because the first one was still queued */ + assert_se(n_rtqueue == 4); + assert_se(last_rtqueue_sigval == 1); + + sd_event_source_unref(u); + sd_event_source_unref(v); + sd_event_source_unref(s); + + sd_event_unref(e); +} + +int main(int argc, char *argv[]) { + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + + test_basic(); + test_sd_event_now(); + test_rtqueue(); + + return 0; +} diff --git a/src/libsystemd/src/sd-hwdb/hwdb-internal.h b/src/libsystemd/src/sd-hwdb/hwdb-internal.h new file mode 100644 index 0000000000..8ffb5e5c74 --- /dev/null +++ b/src/libsystemd/src/sd-hwdb/hwdb-internal.h @@ -0,0 +1,72 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Kay Sievers + + 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 . +***/ + +#include "sparse-endian.h" +#include "util.h" + +#define HWDB_SIG { 'K', 'S', 'L', 'P', 'H', 'H', 'R', 'H' } + +/* on-disk trie objects */ +struct trie_header_f { + uint8_t signature[8]; + + /* version of tool which created the file */ + le64_t tool_version; + le64_t file_size; + + /* size of structures to allow them to grow */ + le64_t header_size; + le64_t node_size; + le64_t child_entry_size; + le64_t value_entry_size; + + /* offset of the root trie node */ + le64_t nodes_root_off; + + /* size of the nodes and string section */ + le64_t nodes_len; + le64_t strings_len; +} _packed_; + +struct trie_node_f { + /* prefix of lookup string, shared by all children */ + le64_t prefix_off; + /* size of children entry array appended to the node */ + uint8_t children_count; + uint8_t padding[7]; + /* size of value entry array appended to the node */ + le64_t values_count; +} _packed_; + +/* array of child entries, follows directly the node record */ +struct trie_child_entry_f { + /* index of the child node */ + uint8_t c; + uint8_t padding[7]; + /* offset of the child node */ + le64_t child_off; +} _packed_; + +/* array of value entries, follows directly the node record/child array */ +struct trie_value_entry_f { + le64_t key_off; + le64_t value_off; +} _packed_; diff --git a/src/libsystemd/src/sd-hwdb/hwdb-util.h b/src/libsystemd/src/sd-hwdb/hwdb-util.h new file mode 100644 index 0000000000..05dc47962b --- /dev/null +++ b/src/libsystemd/src/sd-hwdb/hwdb-util.h @@ -0,0 +1,26 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen + + 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 . +***/ + +#include + +#include "util.h" + +bool hwdb_validate(sd_hwdb *hwdb); diff --git a/src/libsystemd/src/sd-hwdb/sd-hwdb.c b/src/libsystemd/src/sd-hwdb/sd-hwdb.c new file mode 100644 index 0000000000..7dcfe95b87 --- /dev/null +++ b/src/libsystemd/src/sd-hwdb/sd-hwdb.c @@ -0,0 +1,470 @@ +/*** + This file is part of systemd. + + Copyright 2012 Kay Sievers + Copyright 2008 Alan Jenkins + Copyright 2014 Tom Gundersen + + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "fd-util.h" +#include "hashmap.h" +#include "hwdb-internal.h" +#include "hwdb-util.h" +#include "refcnt.h" +#include "string-util.h" + +struct sd_hwdb { + RefCount n_ref; + int refcount; + + FILE *f; + struct stat st; + union { + struct trie_header_f *head; + const char *map; + }; + + char *modalias; + + OrderedHashmap *properties; + Iterator properties_iterator; + bool properties_modified; +}; + +struct linebuf { + char bytes[LINE_MAX]; + size_t size; + size_t len; +}; + +static void linebuf_init(struct linebuf *buf) { + buf->size = 0; + buf->len = 0; +} + +static const char *linebuf_get(struct linebuf *buf) { + if (buf->len + 1 >= sizeof(buf->bytes)) + return NULL; + buf->bytes[buf->len] = '\0'; + return buf->bytes; +} + +static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) { + if (buf->len + len >= sizeof(buf->bytes)) + return false; + memcpy(buf->bytes + buf->len, s, len); + buf->len += len; + return true; +} + +static bool linebuf_add_char(struct linebuf *buf, char c) { + if (buf->len + 1 >= sizeof(buf->bytes)) + return false; + buf->bytes[buf->len++] = c; + return true; +} + +static void linebuf_rem(struct linebuf *buf, size_t count) { + assert(buf->len >= count); + buf->len -= count; +} + +static void linebuf_rem_char(struct linebuf *buf) { + linebuf_rem(buf, 1); +} + +static const struct trie_child_entry_f *trie_node_children(sd_hwdb *hwdb, const struct trie_node_f *node) { + return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size)); +} + +static const struct trie_value_entry_f *trie_node_values(sd_hwdb *hwdb, const struct trie_node_f *node) { + const char *base = (const char *)node; + + base += le64toh(hwdb->head->node_size); + base += node->children_count * le64toh(hwdb->head->child_entry_size); + return (const struct trie_value_entry_f *)base; +} + +static const struct trie_node_f *trie_node_from_off(sd_hwdb *hwdb, le64_t off) { + return (const struct trie_node_f *)(hwdb->map + le64toh(off)); +} + +static const char *trie_string(sd_hwdb *hwdb, le64_t off) { + return hwdb->map + le64toh(off); +} + +static int trie_children_cmp_f(const void *v1, const void *v2) { + const struct trie_child_entry_f *n1 = v1; + const struct trie_child_entry_f *n2 = v2; + + return n1->c - n2->c; +} + +static const struct trie_node_f *node_lookup_f(sd_hwdb *hwdb, const struct trie_node_f *node, uint8_t c) { + struct trie_child_entry_f *child; + struct trie_child_entry_f search; + + search.c = c; + child = bsearch(&search, trie_node_children(hwdb, node), node->children_count, + le64toh(hwdb->head->child_entry_size), trie_children_cmp_f); + if (child) + return trie_node_from_off(hwdb, child->child_off); + return NULL; +} + +static int hwdb_add_property(sd_hwdb *hwdb, const char *key, const char *value) { + int r; + + assert(hwdb); + assert(key); + assert(value); + + /* + * Silently ignore all properties which do not start with a + * space; future extensions might use additional prefixes. + */ + if (key[0] != ' ') + return 0; + + key++; + + r = ordered_hashmap_ensure_allocated(&hwdb->properties, &string_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_replace(hwdb->properties, key, (char*)value); + if (r < 0) + return r; + + hwdb->properties_modified = true; + + return 0; +} + +static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t p, + struct linebuf *buf, const char *search) { + size_t len; + size_t i; + const char *prefix; + int err; + + prefix = trie_string(hwdb, node->prefix_off); + len = strlen(prefix + p); + linebuf_add(buf, prefix + p, len); + + for (i = 0; i < node->children_count; i++) { + const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i]; + + linebuf_add_char(buf, child->c); + err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search); + if (err < 0) + return err; + linebuf_rem_char(buf); + } + + if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0) + for (i = 0; i < le64toh(node->values_count); i++) { + err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off), + trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off)); + if (err < 0) + return err; + } + + linebuf_rem(buf, len); + return 0; +} + +static int trie_search_f(sd_hwdb *hwdb, const char *search) { + struct linebuf buf; + const struct trie_node_f *node; + size_t i = 0; + int err; + + linebuf_init(&buf); + + node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off); + while (node) { + const struct trie_node_f *child; + size_t p = 0; + + if (node->prefix_off) { + uint8_t c; + + for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) { + if (c == '*' || c == '?' || c == '[') + return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p); + if (c != search[i + p]) + return 0; + } + i += p; + } + + child = node_lookup_f(hwdb, node, '*'); + if (child) { + linebuf_add_char(&buf, '*'); + err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i); + if (err < 0) + return err; + linebuf_rem_char(&buf); + } + + child = node_lookup_f(hwdb, node, '?'); + if (child) { + linebuf_add_char(&buf, '?'); + err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i); + if (err < 0) + return err; + linebuf_rem_char(&buf); + } + + child = node_lookup_f(hwdb, node, '['); + if (child) { + linebuf_add_char(&buf, '['); + err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i); + if (err < 0) + return err; + linebuf_rem_char(&buf); + } + + if (search[i] == '\0') { + size_t n; + + for (n = 0; n < le64toh(node->values_count); n++) { + err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off), + trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off)); + if (err < 0) + return err; + } + return 0; + } + + child = node_lookup_f(hwdb, node, search[i]); + node = child; + i++; + } + return 0; +} + +static const char hwdb_bin_paths[] = + "/etc/systemd/hwdb/hwdb.bin\0" + "/etc/udev/hwdb.bin\0" + "/usr/lib/systemd/hwdb/hwdb.bin\0" +#ifdef HAVE_SPLIT_USR + "/lib/systemd/hwdb/hwdb.bin\0" +#endif + UDEVLIBEXECDIR "/hwdb.bin\0"; + +_public_ int sd_hwdb_new(sd_hwdb **ret) { + _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; + const char *hwdb_bin_path; + const char sig[] = HWDB_SIG; + + assert_return(ret, -EINVAL); + + hwdb = new0(sd_hwdb, 1); + if (!hwdb) + return -ENOMEM; + + hwdb->n_ref = REFCNT_INIT; + + /* find hwdb.bin in hwdb_bin_paths */ + NULSTR_FOREACH(hwdb_bin_path, hwdb_bin_paths) { + hwdb->f = fopen(hwdb_bin_path, "re"); + if (hwdb->f) + break; + else if (errno == ENOENT) + continue; + else + return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path); + } + + if (!hwdb->f) { + log_debug("hwdb.bin does not exist, please run udevadm hwdb --update"); + return -ENOENT; + } + + if (fstat(fileno(hwdb->f), &hwdb->st) < 0 || + (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8) + return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path); + + hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0); + if (hwdb->map == MAP_FAILED) + return log_debug_errno(errno, "error mapping %s: %m", hwdb_bin_path); + + if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 || + (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) { + log_debug("error recognizing the format of %s", hwdb_bin_path); + return -EINVAL; + } + + log_debug("=== trie on-disk ==="); + log_debug("tool version: %"PRIu64, le64toh(hwdb->head->tool_version)); + log_debug("file size: %8"PRIi64" bytes", hwdb->st.st_size); + log_debug("header size %8"PRIu64" bytes", le64toh(hwdb->head->header_size)); + log_debug("strings %8"PRIu64" bytes", le64toh(hwdb->head->strings_len)); + log_debug("nodes %8"PRIu64" bytes", le64toh(hwdb->head->nodes_len)); + + *ret = hwdb; + hwdb = NULL; + + return 0; +} + +_public_ sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb) { + assert_return(hwdb, NULL); + + assert_se(REFCNT_INC(hwdb->n_ref) >= 2); + + return hwdb; +} + +_public_ sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb) { + if (hwdb && REFCNT_DEC(hwdb->n_ref) == 0) { + if (hwdb->map) + munmap((void *)hwdb->map, hwdb->st.st_size); + safe_fclose(hwdb->f); + free(hwdb->modalias); + ordered_hashmap_free(hwdb->properties); + free(hwdb); + } + + return NULL; +} + +bool hwdb_validate(sd_hwdb *hwdb) { + bool found = false; + const char* p; + struct stat st; + + if (!hwdb) + return false; + if (!hwdb->f) + return false; + + /* if hwdb.bin doesn't exist anywhere, we need to update */ + NULSTR_FOREACH(p, hwdb_bin_paths) { + if (stat(p, &st) >= 0) { + found = true; + break; + } + } + if (!found) + return true; + + if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim)) + return true; + return false; +} + +static int properties_prepare(sd_hwdb *hwdb, const char *modalias) { + _cleanup_free_ char *mod = NULL; + int r; + + assert(hwdb); + assert(modalias); + + if (streq_ptr(modalias, hwdb->modalias)) + return 0; + + mod = strdup(modalias); + if (!mod) + return -ENOMEM; + + ordered_hashmap_clear(hwdb->properties); + + hwdb->properties_modified = true; + + r = trie_search_f(hwdb, modalias); + if (r < 0) + return r; + + free(hwdb->modalias); + hwdb->modalias = mod; + mod = NULL; + + return 0; +} + +_public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) { + const char *value; + int r; + + assert_return(hwdb, -EINVAL); + assert_return(hwdb->f, -EINVAL); + assert_return(modalias, -EINVAL); + assert_return(_value, -EINVAL); + + r = properties_prepare(hwdb, modalias); + if (r < 0) + return r; + + value = ordered_hashmap_get(hwdb->properties, key); + if (!value) + return -ENOENT; + + *_value = value; + + return 0; +} + +_public_ int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias) { + int r; + + assert_return(hwdb, -EINVAL); + assert_return(hwdb->f, -EINVAL); + assert_return(modalias, -EINVAL); + + r = properties_prepare(hwdb, modalias); + if (r < 0) + return r; + + hwdb->properties_modified = false; + hwdb->properties_iterator = ITERATOR_FIRST; + + return 0; +} + +_public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value) { + const void *k; + void *v; + + assert_return(hwdb, -EINVAL); + assert_return(key, -EINVAL); + assert_return(value, -EINVAL); + + if (hwdb->properties_modified) + return -EAGAIN; + + ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, &v, &k); + if (!k) + return 0; + + *key = k; + *value = v; + + return 1; +} diff --git a/src/libsystemd/src/sd-id128/sd-id128.c b/src/libsystemd/src/sd-id128/sd-id128.c new file mode 100644 index 0000000000..cda3e9f0df --- /dev/null +++ b/src/libsystemd/src/sd-id128/sd-id128.c @@ -0,0 +1,225 @@ +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include +#include + +#include + +#include "fd-util.h" +#include "hexdecoct.h" +#include "io-util.h" +#include "macro.h" +#include "random-util.h" +#include "util.h" + +_public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) { + unsigned n; + + assert_return(s, NULL); + + for (n = 0; n < 16; n++) { + s[n*2] = hexchar(id.bytes[n] >> 4); + s[n*2+1] = hexchar(id.bytes[n] & 0xF); + } + + s[32] = 0; + + return s; +} + +_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) { + unsigned n, i; + sd_id128_t t; + bool is_guid = false; + + assert_return(s, -EINVAL); + assert_return(ret, -EINVAL); + + for (n = 0, i = 0; n < 16;) { + int a, b; + + if (s[i] == '-') { + /* Is this a GUID? Then be nice, and skip over + * the dashes */ + + if (i == 8) + is_guid = true; + else if (i == 13 || i == 18 || i == 23) { + if (!is_guid) + return -EINVAL; + } else + return -EINVAL; + + i++; + continue; + } + + a = unhexchar(s[i++]); + if (a < 0) + return -EINVAL; + + b = unhexchar(s[i++]); + if (b < 0) + return -EINVAL; + + t.bytes[n++] = (a << 4) | b; + } + + if (i != (is_guid ? 36 : 32)) + return -EINVAL; + + if (s[i] != 0) + return -EINVAL; + + *ret = t; + return 0; +} + +static sd_id128_t make_v4_uuid(sd_id128_t id) { + /* Stolen from generate_random_uuid() of drivers/char/random.c + * in the kernel sources */ + + /* Set UUID version to 4 --- truly random generation */ + id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40; + + /* Set the UUID variant to DCE */ + id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80; + + return id; +} + +_public_ int sd_id128_get_machine(sd_id128_t *ret) { + static thread_local sd_id128_t saved_machine_id; + static thread_local bool saved_machine_id_valid = false; + _cleanup_close_ int fd = -1; + char buf[33]; + unsigned j; + sd_id128_t t; + int r; + + assert_return(ret, -EINVAL); + + if (saved_machine_id_valid) { + *ret = saved_machine_id; + return 0; + } + + fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + r = loop_read_exact(fd, buf, 33, false); + if (r < 0) + return r; + if (buf[32] !='\n') + return -EIO; + + for (j = 0; j < 16; j++) { + int a, b; + + a = unhexchar(buf[j*2]); + b = unhexchar(buf[j*2+1]); + + if (a < 0 || b < 0) + return -EIO; + + t.bytes[j] = a << 4 | b; + } + + saved_machine_id = t; + saved_machine_id_valid = true; + + *ret = t; + return 0; +} + +_public_ int sd_id128_get_boot(sd_id128_t *ret) { + static thread_local sd_id128_t saved_boot_id; + static thread_local bool saved_boot_id_valid = false; + _cleanup_close_ int fd = -1; + char buf[36]; + unsigned j; + sd_id128_t t; + char *p; + int r; + + assert_return(ret, -EINVAL); + + if (saved_boot_id_valid) { + *ret = saved_boot_id; + return 0; + } + + fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + r = loop_read_exact(fd, buf, 36, false); + if (r < 0) + return r; + + for (j = 0, p = buf; j < 16; j++) { + int a, b; + + if (p >= buf + 35) + return -EIO; + + if (*p == '-') { + p++; + if (p >= buf + 35) + return -EIO; + } + + a = unhexchar(p[0]); + b = unhexchar(p[1]); + + if (a < 0 || b < 0) + return -EIO; + + t.bytes[j] = a << 4 | b; + + p += 2; + } + + saved_boot_id = t; + saved_boot_id_valid = true; + + *ret = t; + return 0; +} + +_public_ int sd_id128_randomize(sd_id128_t *ret) { + sd_id128_t t; + int r; + + assert_return(ret, -EINVAL); + + r = dev_urandom(&t, sizeof(t)); + if (r < 0) + return r; + + /* Turn this into a valid v4 UUID, to be nice. Note that we + * only guarantee this for newly generated UUIDs, not for + * pre-existing ones. */ + + *ret = make_v4_uuid(t); + return 0; +} diff --git a/src/libsystemd/src/sd-journal/Makefile b/src/libsystemd/src/sd-journal/Makefile new file mode 100644 index 0000000000..4a237512be --- /dev/null +++ b/src/libsystemd/src/sd-journal/Makefile @@ -0,0 +1,37 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# 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 . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +audit_list_includes = -include linux/audit.h -include missing.h +ifneq ($(HAVE_AUDIT),) +audit_list_includes += -include libaudit.h +endif # HAVE_AUDIT + +$(outdir)/audit_type-list.txt: + $(AM_V_GEN)$(CPP) $(ALL_CPPFLAGS) -dM $(audit_list_includes) - $@ + +$(outdir)/audit_type-to-name.h: $(outdir)/audit_type-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "const char *audit_type_to_string(int type) {\n\tswitch(type) {" } {printf " case AUDIT_%s: return \"%s\";\n", $$1, $$1 } END{ print " default: return NULL;\n\t}\n}\n" }' <$< >$@ + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libsystemd/src/sd-journal/audit-type.c b/src/libsystemd/src/sd-journal/audit-type.c new file mode 100644 index 0000000000..71e8790ca8 --- /dev/null +++ b/src/libsystemd/src/sd-journal/audit-type.c @@ -0,0 +1,29 @@ +/*** + This file is part of systemd. + + Copyright 2015 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 . +***/ + +#include +#include +#ifdef HAVE_AUDIT +# include +#endif + +#include "missing.h" +#include "audit-type.h" +#include "audit_type-to-name.h" +#include "macro.h" diff --git a/src/libsystemd/src/sd-journal/audit-type.h b/src/libsystemd/src/sd-journal/audit-type.h new file mode 100644 index 0000000000..1dd2163707 --- /dev/null +++ b/src/libsystemd/src/sd-journal/audit-type.h @@ -0,0 +1,37 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 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 . +***/ + +#include "macro.h" + +const char *audit_type_to_string(int type); +int audit_type_from_string(const char *s); + +/* This is inspired by DNS TYPEnnn formatting */ +#define audit_type_name_alloca(type) \ + ({ \ + const char *_s_; \ + _s_ = audit_type_to_string(type); \ + if (!_s_) { \ + _s_ = alloca(strlen("AUDIT") + DECIMAL_STR_MAX(int)); \ + sprintf((char*) _s_, "AUDIT%04i", type); \ + } \ + _s_; \ + }) diff --git a/src/libsystemd/src/sd-journal/catalog.c b/src/libsystemd/src/sd-journal/catalog.c new file mode 100644 index 0000000000..70838d958c --- /dev/null +++ b/src/libsystemd/src/sd-journal/catalog.c @@ -0,0 +1,767 @@ +/*** + This file is part of systemd. + + Copyright 2012 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "catalog.h" +#include "conf-files.h" +#include "fd-util.h" +#include "fileio.h" +#include "hashmap.h" +#include "log.h" +#include "mkdir.h" +#include "path-util.h" +#include "siphash24.h" +#include "sparse-endian.h" +#include "strbuf.h" +#include "string-util.h" +#include "strv.h" +#include "util.h" + +const char * const catalog_file_dirs[] = { + "/usr/local/lib/systemd/catalog/", + "/usr/lib/systemd/catalog/", + NULL +}; + +#define CATALOG_SIGNATURE (uint8_t[]) { 'R', 'H', 'H', 'H', 'K', 'S', 'L', 'P' } + +typedef struct CatalogHeader { + uint8_t signature[8]; /* "RHHHKSLP" */ + le32_t compatible_flags; + le32_t incompatible_flags; + le64_t header_size; + le64_t n_items; + le64_t catalog_item_size; +} CatalogHeader; + +typedef struct CatalogItem { + sd_id128_t id; + char language[32]; + le64_t offset; +} CatalogItem; + +static void catalog_hash_func(const void *p, struct siphash *state) { + const CatalogItem *i = p; + + siphash24_compress(&i->id, sizeof(i->id), state); + siphash24_compress(i->language, strlen(i->language), state); +} + +static int catalog_compare_func(const void *a, const void *b) { + const CatalogItem *i = a, *j = b; + unsigned k; + + for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) { + if (i->id.bytes[k] < j->id.bytes[k]) + return -1; + if (i->id.bytes[k] > j->id.bytes[k]) + return 1; + } + + return strcmp(i->language, j->language); +} + +const struct hash_ops catalog_hash_ops = { + .hash = catalog_hash_func, + .compare = catalog_compare_func +}; + +static bool next_header(const char **s) { + const char *e; + + e = strchr(*s, '\n'); + + /* Unexpected end */ + if (!e) + return false; + + /* End of headers */ + if (e == *s) + return false; + + *s = e + 1; + return true; +} + +static const char *skip_header(const char *s) { + while (next_header(&s)) + ; + return s; +} + +static char *combine_entries(const char *one, const char *two) { + const char *b1, *b2; + size_t l1, l2, n; + char *dest, *p; + + /* Find split point of headers to body */ + b1 = skip_header(one); + b2 = skip_header(two); + + l1 = strlen(one); + l2 = strlen(two); + dest = new(char, l1 + l2 + 1); + if (!dest) { + log_oom(); + return NULL; + } + + p = dest; + + /* Headers from @one */ + n = b1 - one; + p = mempcpy(p, one, n); + + /* Headers from @two, these will only be found if not present above */ + n = b2 - two; + p = mempcpy(p, two, n); + + /* Body from @one */ + n = l1 - (b1 - one); + if (n > 0) { + memcpy(p, b1, n); + p += n; + + /* Body from @two */ + } else { + n = l2 - (b2 - two); + memcpy(p, b2, n); + p += n; + } + + assert(p - dest <= (ptrdiff_t)(l1 + l2)); + p[0] = '\0'; + return dest; +} + +static int finish_item( + Hashmap *h, + sd_id128_t id, + const char *language, + char *payload, size_t payload_size) { + + _cleanup_free_ CatalogItem *i = NULL; + _cleanup_free_ char *prev = NULL, *combined = NULL; + + assert(h); + assert(payload); + assert(payload_size > 0); + + i = new0(CatalogItem, 1); + if (!i) + return log_oom(); + + i->id = id; + if (language) { + assert(strlen(language) > 1 && strlen(language) < 32); + strcpy(i->language, language); + } + + prev = hashmap_get(h, i); + if (prev) { + /* Already have such an item, combine them */ + combined = combine_entries(payload, prev); + if (!combined) + return log_oom(); + + if (hashmap_update(h, i, combined) < 0) + return log_oom(); + combined = NULL; + } else { + /* A new item */ + combined = memdup(payload, payload_size + 1); + if (!combined) + return log_oom(); + + if (hashmap_put(h, i, combined) < 0) + return log_oom(); + i = NULL; + combined = NULL; + } + + return 0; +} + +int catalog_file_lang(const char* filename, char **lang) { + char *beg, *end, *_lang; + + end = endswith(filename, ".catalog"); + if (!end) + return 0; + + beg = end - 1; + while (beg > filename && *beg != '.' && *beg != '/' && end - beg < 32) + beg--; + + if (*beg != '.' || end <= beg + 1) + return 0; + + _lang = strndup(beg + 1, end - beg - 1); + if (!_lang) + return -ENOMEM; + + *lang = _lang; + return 1; +} + +static int catalog_entry_lang(const char* filename, int line, + const char* t, const char* deflang, char **lang) { + size_t c; + + c = strlen(t); + if (c == 0) { + log_error("[%s:%u] Language too short.", filename, line); + return -EINVAL; + } + if (c > 31) { + log_error("[%s:%u] language too long.", filename, line); + return -EINVAL; + } + + if (deflang) { + if (streq(t, deflang)) { + log_warning("[%s:%u] language specified unnecessarily", + filename, line); + return 0; + } else + log_warning("[%s:%u] language differs from default for file", + filename, line); + } + + *lang = strdup(t); + if (!*lang) + return -ENOMEM; + + return 0; +} + +int catalog_import_file(Hashmap *h, const char *path) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *payload = NULL; + size_t payload_size = 0, payload_allocated = 0; + unsigned n = 0; + sd_id128_t id; + _cleanup_free_ char *deflang = NULL, *lang = NULL; + bool got_id = false, empty_line = true; + int r; + + assert(h); + assert(path); + + f = fopen(path, "re"); + if (!f) + return log_error_errno(errno, "Failed to open file %s: %m", path); + + r = catalog_file_lang(path, &deflang); + if (r < 0) + log_error_errno(r, "Failed to determine language for file %s: %m", path); + if (r == 1) + log_debug("File %s has language %s.", path, deflang); + + for (;;) { + char line[LINE_MAX]; + size_t line_len; + + if (!fgets(line, sizeof(line), f)) { + if (feof(f)) + break; + + return log_error_errno(errno, "Failed to read file %s: %m", path); + } + + n++; + + truncate_nl(line); + + if (line[0] == 0) { + empty_line = true; + continue; + } + + if (strchr(COMMENTS "\n", line[0])) + continue; + + if (empty_line && + strlen(line) >= 2+1+32 && + line[0] == '-' && + line[1] == '-' && + line[2] == ' ' && + (line[2+1+32] == ' ' || line[2+1+32] == '\0')) { + + bool with_language; + sd_id128_t jd; + + /* New entry */ + + with_language = line[2+1+32] != '\0'; + line[2+1+32] = '\0'; + + if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) { + + if (got_id) { + if (payload_size == 0) { + log_error("[%s:%u] No payload text.", path, n); + return -EINVAL; + } + + r = finish_item(h, id, lang ?: deflang, payload, payload_size); + if (r < 0) + return r; + + lang = mfree(lang); + payload_size = 0; + } + + if (with_language) { + char *t; + + t = strstrip(line + 2 + 1 + 32 + 1); + r = catalog_entry_lang(path, n, t, deflang, &lang); + if (r < 0) + return r; + } + + got_id = true; + empty_line = false; + id = jd; + + continue; + } + } + + /* Payload */ + if (!got_id) { + log_error("[%s:%u] Got payload before ID.", path, n); + return -EINVAL; + } + + line_len = strlen(line); + if (!GREEDY_REALLOC(payload, payload_allocated, + payload_size + (empty_line ? 1 : 0) + line_len + 1 + 1)) + return log_oom(); + + if (empty_line) + payload[payload_size++] = '\n'; + memcpy(payload + payload_size, line, line_len); + payload_size += line_len; + payload[payload_size++] = '\n'; + payload[payload_size] = '\0'; + + empty_line = false; + } + + if (got_id) { + if (payload_size == 0) { + log_error("[%s:%u] No payload text.", path, n); + return -EINVAL; + } + + r = finish_item(h, id, lang ?: deflang, payload, payload_size); + if (r < 0) + return r; + } + + return 0; +} + +static int64_t write_catalog(const char *database, struct strbuf *sb, + CatalogItem *items, size_t n) { + CatalogHeader header; + _cleanup_fclose_ FILE *w = NULL; + int r; + _cleanup_free_ char *d, *p = NULL; + size_t k; + + d = dirname_malloc(database); + if (!d) + return log_oom(); + + r = mkdir_p(d, 0775); + if (r < 0) + return log_error_errno(r, "Recursive mkdir %s: %m", d); + + r = fopen_temporary(database, &w, &p); + if (r < 0) + return log_error_errno(r, "Failed to open database for writing: %s: %m", + database); + + zero(header); + memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature)); + header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8)); + header.catalog_item_size = htole64(sizeof(CatalogItem)); + header.n_items = htole64(n); + + r = -EIO; + + k = fwrite(&header, 1, sizeof(header), w); + if (k != sizeof(header)) { + log_error("%s: failed to write header.", p); + goto error; + } + + k = fwrite(items, 1, n * sizeof(CatalogItem), w); + if (k != n * sizeof(CatalogItem)) { + log_error("%s: failed to write database.", p); + goto error; + } + + k = fwrite(sb->buf, 1, sb->len, w); + if (k != sb->len) { + log_error("%s: failed to write strings.", p); + goto error; + } + + r = fflush_and_check(w); + if (r < 0) { + log_error_errno(r, "%s: failed to write database: %m", p); + goto error; + } + + fchmod(fileno(w), 0644); + + if (rename(p, database) < 0) { + r = log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database); + goto error; + } + + return ftello(w); + +error: + (void) unlink(p); + return r; +} + +int catalog_update(const char* database, const char* root, const char* const* dirs) { + _cleanup_strv_free_ char **files = NULL; + char **f; + struct strbuf *sb = NULL; + _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; + _cleanup_free_ CatalogItem *items = NULL; + ssize_t offset; + char *payload; + CatalogItem *i; + Iterator j; + unsigned n; + int r; + int64_t sz; + + h = hashmap_new(&catalog_hash_ops); + sb = strbuf_new(); + + if (!h || !sb) { + r = log_oom(); + goto finish; + } + + r = conf_files_list_strv(&files, ".catalog", root, dirs); + if (r < 0) { + log_error_errno(r, "Failed to get catalog files: %m"); + goto finish; + } + + STRV_FOREACH(f, files) { + log_debug("Reading file '%s'", *f); + r = catalog_import_file(h, *f); + if (r < 0) { + log_error_errno(r, "Failed to import file '%s': %m", *f); + goto finish; + } + } + + if (hashmap_size(h) <= 0) { + log_info("No items in catalog."); + goto finish; + } else + log_debug("Found %u items in catalog.", hashmap_size(h)); + + items = new(CatalogItem, hashmap_size(h)); + if (!items) { + r = log_oom(); + goto finish; + } + + n = 0; + HASHMAP_FOREACH_KEY(payload, i, h, j) { + log_debug("Found " SD_ID128_FORMAT_STR ", language %s", + SD_ID128_FORMAT_VAL(i->id), + isempty(i->language) ? "C" : i->language); + + offset = strbuf_add_string(sb, payload, strlen(payload)); + if (offset < 0) { + r = log_oom(); + goto finish; + } + i->offset = htole64((uint64_t) offset); + items[n++] = *i; + } + + assert(n == hashmap_size(h)); + qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func); + + strbuf_complete(sb); + + sz = write_catalog(database, sb, items, n); + if (sz < 0) + r = log_error_errno(sz, "Failed to write %s: %m", database); + else { + r = 0; + log_debug("%s: wrote %u items, with %zu bytes of strings, %"PRIi64" total size.", + database, n, sb->len, sz); + } + +finish: + strbuf_cleanup(sb); + + return r; +} + +static int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) { + const CatalogHeader *h; + int fd; + void *p; + struct stat st; + + assert(_fd); + assert(_st); + assert(_p); + + fd = open(database, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return -errno; + + if (fstat(fd, &st) < 0) { + safe_close(fd); + return -errno; + } + + if (st.st_size < (off_t) sizeof(CatalogHeader)) { + safe_close(fd); + return -EINVAL; + } + + p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) { + safe_close(fd); + return -errno; + } + + h = p; + if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 || + le64toh(h->header_size) < sizeof(CatalogHeader) || + le64toh(h->catalog_item_size) < sizeof(CatalogItem) || + h->incompatible_flags != 0 || + le64toh(h->n_items) <= 0 || + st.st_size < (off_t) (le64toh(h->header_size) + le64toh(h->catalog_item_size) * le64toh(h->n_items))) { + safe_close(fd); + munmap(p, st.st_size); + return -EBADMSG; + } + + *_fd = fd; + *_st = st; + *_p = p; + + return 0; +} + +static const char *find_id(void *p, sd_id128_t id) { + CatalogItem key, *f = NULL; + const CatalogHeader *h = p; + const char *loc; + + zero(key); + key.id = id; + + loc = setlocale(LC_MESSAGES, NULL); + if (loc && loc[0] && !streq(loc, "C") && !streq(loc, "POSIX")) { + strncpy(key.language, loc, sizeof(key.language)); + key.language[strcspn(key.language, ".@")] = 0; + + f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func); + if (!f) { + char *e; + + e = strchr(key.language, '_'); + if (e) { + *e = 0; + f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func); + } + } + } + + if (!f) { + zero(key.language); + f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func); + } + + if (!f) + return NULL; + + return (const char*) p + + le64toh(h->header_size) + + le64toh(h->n_items) * le64toh(h->catalog_item_size) + + le64toh(f->offset); +} + +int catalog_get(const char* database, sd_id128_t id, char **_text) { + _cleanup_close_ int fd = -1; + void *p = NULL; + struct stat st = {}; + char *text = NULL; + int r; + const char *s; + + assert(_text); + + r = open_mmap(database, &fd, &st, &p); + if (r < 0) + return r; + + s = find_id(p, id); + if (!s) { + r = -ENOENT; + goto finish; + } + + text = strdup(s); + if (!text) { + r = -ENOMEM; + goto finish; + } + + *_text = text; + r = 0; + +finish: + if (p) + munmap(p, st.st_size); + + return r; +} + +static char *find_header(const char *s, const char *header) { + + for (;;) { + const char *v; + + v = startswith(s, header); + if (v) { + v += strspn(v, WHITESPACE); + return strndup(v, strcspn(v, NEWLINE)); + } + + if (!next_header(&s)) + return NULL; + } +} + +static void dump_catalog_entry(FILE *f, sd_id128_t id, const char *s, bool oneline) { + if (oneline) { + _cleanup_free_ char *subject = NULL, *defined_by = NULL; + + subject = find_header(s, "Subject:"); + defined_by = find_header(s, "Defined-By:"); + + fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n", + SD_ID128_FORMAT_VAL(id), + strna(defined_by), strna(subject)); + } else + fprintf(f, "-- " SD_ID128_FORMAT_STR "\n%s\n", + SD_ID128_FORMAT_VAL(id), s); +} + + +int catalog_list(FILE *f, const char *database, bool oneline) { + _cleanup_close_ int fd = -1; + void *p = NULL; + struct stat st; + const CatalogHeader *h; + const CatalogItem *items; + int r; + unsigned n; + sd_id128_t last_id; + bool last_id_set = false; + + r = open_mmap(database, &fd, &st, &p); + if (r < 0) + return r; + + h = p; + items = (const CatalogItem*) ((const uint8_t*) p + le64toh(h->header_size)); + + for (n = 0; n < le64toh(h->n_items); n++) { + const char *s; + + if (last_id_set && sd_id128_equal(last_id, items[n].id)) + continue; + + assert_se(s = find_id(p, items[n].id)); + + dump_catalog_entry(f, items[n].id, s, oneline); + + last_id_set = true; + last_id = items[n].id; + } + + munmap(p, st.st_size); + + return 0; +} + +int catalog_list_items(FILE *f, const char *database, bool oneline, char **items) { + char **item; + int r = 0; + + STRV_FOREACH(item, items) { + sd_id128_t id; + int k; + _cleanup_free_ char *msg = NULL; + + k = sd_id128_from_string(*item, &id); + if (k < 0) { + log_error_errno(k, "Failed to parse id128 '%s': %m", *item); + if (r == 0) + r = k; + continue; + } + + k = catalog_get(database, id, &msg); + if (k < 0) { + log_full_errno(k == -ENOENT ? LOG_NOTICE : LOG_ERR, k, + "Failed to retrieve catalog entry for '%s': %m", *item); + if (r == 0) + r = k; + continue; + } + + dump_catalog_entry(f, id, msg, oneline); + } + + return r; +} diff --git a/src/libsystemd/src/sd-journal/catalog.h b/src/libsystemd/src/sd-journal/catalog.h new file mode 100644 index 0000000000..b621de3068 --- /dev/null +++ b/src/libsystemd/src/sd-journal/catalog.h @@ -0,0 +1,36 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 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 . +***/ + +#include + +#include + +#include "hashmap.h" +#include "strbuf.h" + +int catalog_import_file(Hashmap *h, const char *path); +int catalog_update(const char* database, const char* root, const char* const* dirs); +int catalog_get(const char* database, sd_id128_t id, char **data); +int catalog_list(FILE *f, const char* database, bool oneline); +int catalog_list_items(FILE *f, const char* database, bool oneline, char **items); +int catalog_file_lang(const char *filename, char **lang); +extern const char * const catalog_file_dirs[]; +extern const struct hash_ops catalog_hash_ops; diff --git a/src/libsystemd/src/sd-journal/compress.c b/src/libsystemd/src/sd-journal/compress.c new file mode 100644 index 0000000000..ba734b5561 --- /dev/null +++ b/src/libsystemd/src/sd-journal/compress.c @@ -0,0 +1,683 @@ +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include +#include +#include +#include + +#ifdef HAVE_XZ +#include +#endif + +#ifdef HAVE_LZ4 +#include +#include +#endif + +#include "alloc-util.h" +#include "compress.h" +#include "fd-util.h" +#include "io-util.h" +#include "journal-def.h" +#include "macro.h" +#include "sparse-endian.h" +#include "string-table.h" +#include "string-util.h" +#include "util.h" + +#ifdef HAVE_LZ4 +DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t, LZ4F_freeCompressionContext); +DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext); +#endif + +#define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t)) + +static const char* const object_compressed_table[_OBJECT_COMPRESSED_MAX] = { + [OBJECT_COMPRESSED_XZ] = "XZ", + [OBJECT_COMPRESSED_LZ4] = "LZ4", +}; + +DEFINE_STRING_TABLE_LOOKUP(object_compressed, int); + +int compress_blob_xz(const void *src, uint64_t src_size, + void *dst, size_t dst_alloc_size, size_t *dst_size) { +#ifdef HAVE_XZ + static const lzma_options_lzma opt = { + 1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT, + LZMA_PB_DEFAULT, LZMA_MODE_FAST, 128, LZMA_MF_HC3, 4 + }; + static const lzma_filter filters[] = { + { LZMA_FILTER_LZMA2, (lzma_options_lzma*) &opt }, + { LZMA_VLI_UNKNOWN, NULL } + }; + lzma_ret ret; + size_t out_pos = 0; + + assert(src); + assert(src_size > 0); + assert(dst); + assert(dst_alloc_size > 0); + assert(dst_size); + + /* Returns < 0 if we couldn't compress the data or the + * compressed result is longer than the original */ + + if (src_size < 80) + return -ENOBUFS; + + ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL, + src, src_size, dst, &out_pos, dst_alloc_size); + if (ret != LZMA_OK) + return -ENOBUFS; + + *dst_size = out_pos; + return 0; +#else + return -EPROTONOSUPPORT; +#endif +} + +int compress_blob_lz4(const void *src, uint64_t src_size, + void *dst, size_t dst_alloc_size, size_t *dst_size) { +#ifdef HAVE_LZ4 + int r; + + assert(src); + assert(src_size > 0); + assert(dst); + assert(dst_alloc_size > 0); + assert(dst_size); + + /* Returns < 0 if we couldn't compress the data or the + * compressed result is longer than the original */ + + if (src_size < 9) + return -ENOBUFS; + + r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8); + if (r <= 0) + return -ENOBUFS; + + *(le64_t*) dst = htole64(src_size); + *dst_size = r + 8; + + return 0; +#else + return -EPROTONOSUPPORT; +#endif +} + + +int decompress_blob_xz(const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { + +#ifdef HAVE_XZ + _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; + lzma_ret ret; + size_t space; + + assert(src); + assert(src_size > 0); + assert(dst); + assert(dst_alloc_size); + assert(dst_size); + assert(*dst_alloc_size == 0 || *dst); + + ret = lzma_stream_decoder(&s, UINT64_MAX, 0); + if (ret != LZMA_OK) + return -ENOMEM; + + space = MIN(src_size * 2, dst_max ?: (size_t) -1); + if (!greedy_realloc(dst, dst_alloc_size, space, 1)) + return -ENOMEM; + + s.next_in = src; + s.avail_in = src_size; + + s.next_out = *dst; + s.avail_out = space; + + for (;;) { + size_t used; + + ret = lzma_code(&s, LZMA_FINISH); + + if (ret == LZMA_STREAM_END) + break; + else if (ret != LZMA_OK) + return -ENOMEM; + + if (dst_max > 0 && (space - s.avail_out) >= dst_max) + break; + else if (dst_max > 0 && space == dst_max) + return -ENOBUFS; + + used = space - s.avail_out; + space = MIN(2 * space, dst_max ?: (size_t) -1); + if (!greedy_realloc(dst, dst_alloc_size, space, 1)) + return -ENOMEM; + + s.avail_out = space - used; + s.next_out = *(uint8_t**)dst + used; + } + + *dst_size = space - s.avail_out; + return 0; +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_blob_lz4(const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { + +#ifdef HAVE_LZ4 + char* out; + int r, size; /* LZ4 uses int for size */ + + assert(src); + assert(src_size > 0); + assert(dst); + assert(dst_alloc_size); + assert(dst_size); + assert(*dst_alloc_size == 0 || *dst); + + if (src_size <= 8) + return -EBADMSG; + + size = le64toh( *(le64_t*)src ); + if (size < 0 || (unsigned) size != le64toh(*(le64_t*)src)) + return -EFBIG; + if ((size_t) size > *dst_alloc_size) { + out = realloc(*dst, size); + if (!out) + return -ENOMEM; + *dst = out; + *dst_alloc_size = size; + } else + out = *dst; + + r = LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size); + if (r < 0 || r != size) + return -EBADMSG; + + *dst_size = size; + return 0; +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_blob(int compression, + const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { + if (compression == OBJECT_COMPRESSED_XZ) + return decompress_blob_xz(src, src_size, + dst, dst_alloc_size, dst_size, dst_max); + else if (compression == OBJECT_COMPRESSED_LZ4) + return decompress_blob_lz4(src, src_size, + dst, dst_alloc_size, dst_size, dst_max); + else + return -EBADMSG; +} + + +int decompress_startswith_xz(const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra) { + +#ifdef HAVE_XZ + _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; + lzma_ret ret; + + /* Checks whether the decompressed blob starts with the + * mentioned prefix. The byte extra needs to follow the + * prefix */ + + assert(src); + assert(src_size > 0); + assert(buffer); + assert(buffer_size); + assert(prefix); + assert(*buffer_size == 0 || *buffer); + + ret = lzma_stream_decoder(&s, UINT64_MAX, 0); + if (ret != LZMA_OK) + return -EBADMSG; + + if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1))) + return -ENOMEM; + + s.next_in = src; + s.avail_in = src_size; + + s.next_out = *buffer; + s.avail_out = *buffer_size; + + for (;;) { + ret = lzma_code(&s, LZMA_FINISH); + + if (ret != LZMA_STREAM_END && ret != LZMA_OK) + return -EBADMSG; + + if (*buffer_size - s.avail_out >= prefix_len + 1) + return memcmp(*buffer, prefix, prefix_len) == 0 && + ((const uint8_t*) *buffer)[prefix_len] == extra; + + if (ret == LZMA_STREAM_END) + return 0; + + s.avail_out += *buffer_size; + + if (!(greedy_realloc(buffer, buffer_size, *buffer_size * 2, 1))) + return -ENOMEM; + + s.next_out = *(uint8_t**)buffer + *buffer_size - s.avail_out; + } + +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_startswith_lz4(const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra) { +#ifdef HAVE_LZ4 + /* Checks whether the decompressed blob starts with the + * mentioned prefix. The byte extra needs to follow the + * prefix */ + + int r; + size_t size; + + assert(src); + assert(src_size > 0); + assert(buffer); + assert(buffer_size); + assert(prefix); + assert(*buffer_size == 0 || *buffer); + + if (src_size <= 8) + return -EBADMSG; + + if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1))) + return -ENOMEM; + + r = LZ4_decompress_safe_partial((char*)src + 8, *buffer, src_size - 8, + prefix_len + 1, *buffer_size); + if (r >= 0) + size = (unsigned) r; + else { + /* lz4 always tries to decode full "sequence", so in + * pathological cases might need to decompress the + * full field. */ + r = decompress_blob_lz4(src, src_size, buffer, buffer_size, &size, 0); + if (r < 0) + return r; + } + + if (size >= prefix_len + 1) + return memcmp(*buffer, prefix, prefix_len) == 0 && + ((const uint8_t*) *buffer)[prefix_len] == extra; + else + return 0; + +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_startswith(int compression, + const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra) { + if (compression == OBJECT_COMPRESSED_XZ) + return decompress_startswith_xz(src, src_size, + buffer, buffer_size, + prefix, prefix_len, + extra); + else if (compression == OBJECT_COMPRESSED_LZ4) + return decompress_startswith_lz4(src, src_size, + buffer, buffer_size, + prefix, prefix_len, + extra); + else + return -EBADMSG; +} + +int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { +#ifdef HAVE_XZ + _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; + lzma_ret ret; + uint8_t buf[BUFSIZ], out[BUFSIZ]; + lzma_action action = LZMA_RUN; + + assert(fdf >= 0); + assert(fdt >= 0); + + ret = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64); + if (ret != LZMA_OK) { + log_error("Failed to initialize XZ encoder: code %u", ret); + return -EINVAL; + } + + for (;;) { + if (s.avail_in == 0 && action == LZMA_RUN) { + size_t m = sizeof(buf); + ssize_t n; + + if (max_bytes != (uint64_t) -1 && (uint64_t) m > max_bytes) + m = (size_t) max_bytes; + + n = read(fdf, buf, m); + if (n < 0) + return -errno; + if (n == 0) + action = LZMA_FINISH; + else { + s.next_in = buf; + s.avail_in = n; + + if (max_bytes != (uint64_t) -1) { + assert(max_bytes >= (uint64_t) n); + max_bytes -= n; + } + } + } + + if (s.avail_out == 0) { + s.next_out = out; + s.avail_out = sizeof(out); + } + + ret = lzma_code(&s, action); + if (ret != LZMA_OK && ret != LZMA_STREAM_END) { + log_error("Compression failed: code %u", ret); + return -EBADMSG; + } + + if (s.avail_out == 0 || ret == LZMA_STREAM_END) { + ssize_t n, k; + + n = sizeof(out) - s.avail_out; + + k = loop_write(fdt, out, n, false); + if (k < 0) + return k; + + if (ret == LZMA_STREAM_END) { + log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)", + s.total_in, s.total_out, + (double) s.total_out / s.total_in * 100); + + return 0; + } + } + } +#else + return -EPROTONOSUPPORT; +#endif +} + +#define LZ4_BUFSIZE (512*1024u) + +int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) { + +#ifdef HAVE_LZ4 + LZ4F_errorCode_t c; + _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL; + _cleanup_free_ char *buf = NULL; + char *src = NULL; + size_t size, n, total_in = 0, total_out, offset = 0, frame_size; + struct stat st; + int r; + static const LZ4F_compressOptions_t options = { + .stableSrc = 1, + }; + static const LZ4F_preferences_t preferences = { + .frameInfo.blockSizeID = 5, + }; + + c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); + if (LZ4F_isError(c)) + return -ENOMEM; + + if (fstat(fdf, &st) < 0) + return log_debug_errno(errno, "fstat() failed: %m"); + + frame_size = LZ4F_compressBound(LZ4_BUFSIZE, &preferences); + size = frame_size + 64*1024; /* add some space for header and trailer */ + buf = malloc(size); + if (!buf) + return -ENOMEM; + + n = offset = total_out = LZ4F_compressBegin(ctx, buf, size, &preferences); + if (LZ4F_isError(n)) + return -EINVAL; + + src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdf, 0); + if (src == MAP_FAILED) + return -errno; + + log_debug("Buffer size is %zu bytes, header size %zu bytes.", size, n); + + while (total_in < (size_t) st.st_size) { + ssize_t k; + + k = MIN(LZ4_BUFSIZE, st.st_size - total_in); + n = LZ4F_compressUpdate(ctx, buf + offset, size - offset, + src + total_in, k, &options); + if (LZ4F_isError(n)) { + r = -ENOTRECOVERABLE; + goto cleanup; + } + + total_in += k; + offset += n; + total_out += n; + + if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) { + log_debug("Compressed stream longer than %"PRIu64" bytes", max_bytes); + return -EFBIG; + } + + if (size - offset < frame_size + 4) { + k = loop_write(fdt, buf, offset, false); + if (k < 0) { + r = k; + goto cleanup; + } + offset = 0; + } + } + + n = LZ4F_compressEnd(ctx, buf + offset, size - offset, &options); + if (LZ4F_isError(n)) { + r = -ENOTRECOVERABLE; + goto cleanup; + } + + offset += n; + total_out += n; + r = loop_write(fdt, buf, offset, false); + if (r < 0) + goto cleanup; + + log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)", + total_in, total_out, + (double) total_out / total_in * 100); + cleanup: + munmap(src, st.st_size); + return r; +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { + +#ifdef HAVE_XZ + _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; + lzma_ret ret; + + uint8_t buf[BUFSIZ], out[BUFSIZ]; + lzma_action action = LZMA_RUN; + + assert(fdf >= 0); + assert(fdt >= 0); + + ret = lzma_stream_decoder(&s, UINT64_MAX, 0); + if (ret != LZMA_OK) { + log_debug("Failed to initialize XZ decoder: code %u", ret); + return -ENOMEM; + } + + for (;;) { + if (s.avail_in == 0 && action == LZMA_RUN) { + ssize_t n; + + n = read(fdf, buf, sizeof(buf)); + if (n < 0) + return -errno; + if (n == 0) + action = LZMA_FINISH; + else { + s.next_in = buf; + s.avail_in = n; + } + } + + if (s.avail_out == 0) { + s.next_out = out; + s.avail_out = sizeof(out); + } + + ret = lzma_code(&s, action); + if (ret != LZMA_OK && ret != LZMA_STREAM_END) { + log_debug("Decompression failed: code %u", ret); + return -EBADMSG; + } + + if (s.avail_out == 0 || ret == LZMA_STREAM_END) { + ssize_t n, k; + + n = sizeof(out) - s.avail_out; + + if (max_bytes != (uint64_t) -1) { + if (max_bytes < (uint64_t) n) + return -EFBIG; + + max_bytes -= n; + } + + k = loop_write(fdt, out, n, false); + if (k < 0) + return k; + + if (ret == LZMA_STREAM_END) { + log_debug("XZ decompression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)", + s.total_in, s.total_out, + (double) s.total_out / s.total_in * 100); + + return 0; + } + } + } +#else + log_debug("Cannot decompress file. Compiled without XZ support."); + return -EPROTONOSUPPORT; +#endif +} + +int decompress_stream_lz4(int in, int out, uint64_t max_bytes) { +#ifdef HAVE_LZ4 + size_t c; + _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL; + _cleanup_free_ char *buf = NULL; + char *src; + struct stat st; + int r = 0; + size_t total_in = 0, total_out = 0; + + c = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); + if (LZ4F_isError(c)) + return -ENOMEM; + + if (fstat(in, &st) < 0) + return log_debug_errno(errno, "fstat() failed: %m"); + + buf = malloc(LZ4_BUFSIZE); + if (!buf) + return -ENOMEM; + + src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0); + if (src == MAP_FAILED) + return -errno; + + while (total_in < (size_t) st.st_size) { + size_t produced = LZ4_BUFSIZE; + size_t used = st.st_size - total_in; + + c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL); + if (LZ4F_isError(c)) { + r = -EBADMSG; + goto cleanup; + } + + total_in += used; + total_out += produced; + + if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) { + log_debug("Decompressed stream longer than %"PRIu64" bytes", max_bytes); + r = -EFBIG; + goto cleanup; + } + + r = loop_write(out, buf, produced, false); + if (r < 0) + goto cleanup; + } + + log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)", + total_in, total_out, + (double) total_out / total_in * 100); + cleanup: + munmap(src, st.st_size); + return r; +#else + log_debug("Cannot decompress file. Compiled without LZ4 support."); + return -EPROTONOSUPPORT; +#endif +} + +int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes) { + + if (endswith(filename, ".lz4")) + return decompress_stream_lz4(fdf, fdt, max_bytes); + else if (endswith(filename, ".xz")) + return decompress_stream_xz(fdf, fdt, max_bytes); + else + return -EPROTONOSUPPORT; +} diff --git a/src/libsystemd/src/sd-journal/compress.h b/src/libsystemd/src/sd-journal/compress.h new file mode 100644 index 0000000000..c138099d9a --- /dev/null +++ b/src/libsystemd/src/sd-journal/compress.h @@ -0,0 +1,85 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include + +#include "journal-def.h" + +const char* object_compressed_to_string(int compression); +int object_compressed_from_string(const char *compression); + +int compress_blob_xz(const void *src, uint64_t src_size, + void *dst, size_t dst_alloc_size, size_t *dst_size); +int compress_blob_lz4(const void *src, uint64_t src_size, + void *dst, size_t dst_alloc_size, size_t *dst_size); + +static inline int compress_blob(const void *src, uint64_t src_size, + void *dst, size_t dst_alloc_size, size_t *dst_size) { + int r; +#ifdef HAVE_LZ4 + r = compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size); + if (r == 0) + return OBJECT_COMPRESSED_LZ4; +#else + r = compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size); + if (r == 0) + return OBJECT_COMPRESSED_XZ; +#endif + return r; +} + +int decompress_blob_xz(const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); +int decompress_blob_lz4(const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); +int decompress_blob(int compression, + const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); + +int decompress_startswith_xz(const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra); +int decompress_startswith_lz4(const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra); +int decompress_startswith(int compression, + const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra); + +int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes); +int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes); + +int decompress_stream_xz(int fdf, int fdt, uint64_t max_size); +int decompress_stream_lz4(int fdf, int fdt, uint64_t max_size); + +#ifdef HAVE_LZ4 +# define compress_stream compress_stream_lz4 +# define COMPRESSED_EXT ".lz4" +#else +# define compress_stream compress_stream_xz +# define COMPRESSED_EXT ".xz" +#endif + +int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes); diff --git a/src/libsystemd/src/sd-journal/fsprg.c b/src/libsystemd/src/sd-journal/fsprg.c new file mode 100644 index 0000000000..612b10f3a9 --- /dev/null +++ b/src/libsystemd/src/sd-journal/fsprg.c @@ -0,0 +1,374 @@ +/* + * fsprg v0.1 - (seekable) forward-secure pseudorandom generator + * Copyright (C) 2012 B. Poettering + * Contact: fsprg@point-at-infinity.org + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +/* + * See "Practical Secure Logging: Seekable Sequential Key Generators" + * by G. A. Marson, B. Poettering for details: + * + * http://eprint.iacr.org/2013/397 + */ + +#include +#include + +#include "fsprg.h" +#include "gcrypt-util.h" + +#define ISVALID_SECPAR(secpar) (((secpar) % 16 == 0) && ((secpar) >= 16) && ((secpar) <= 16384)) +#define VALIDATE_SECPAR(secpar) assert(ISVALID_SECPAR(secpar)); + +#define RND_HASH GCRY_MD_SHA256 +#define RND_GEN_P 0x01 +#define RND_GEN_Q 0x02 +#define RND_GEN_X 0x03 + +/******************************************************************************/ + +static void mpi_export(void *buf, size_t buflen, const gcry_mpi_t x) { + unsigned len; + size_t nwritten; + + assert(gcry_mpi_cmp_ui(x, 0) >= 0); + len = (gcry_mpi_get_nbits(x) + 7) / 8; + assert(len <= buflen); + memzero(buf, buflen); + gcry_mpi_print(GCRYMPI_FMT_USG, buf + (buflen - len), len, &nwritten, x); + assert(nwritten == len); +} + +static gcry_mpi_t mpi_import(const void *buf, size_t buflen) { + gcry_mpi_t h; + unsigned len; + + assert_se(gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL) == 0); + len = (gcry_mpi_get_nbits(h) + 7) / 8; + assert(len <= buflen); + assert(gcry_mpi_cmp_ui(h, 0) >= 0); + + return h; +} + +static void uint64_export(void *buf, size_t buflen, uint64_t x) { + assert(buflen == 8); + ((uint8_t*) buf)[0] = (x >> 56) & 0xff; + ((uint8_t*) buf)[1] = (x >> 48) & 0xff; + ((uint8_t*) buf)[2] = (x >> 40) & 0xff; + ((uint8_t*) buf)[3] = (x >> 32) & 0xff; + ((uint8_t*) buf)[4] = (x >> 24) & 0xff; + ((uint8_t*) buf)[5] = (x >> 16) & 0xff; + ((uint8_t*) buf)[6] = (x >> 8) & 0xff; + ((uint8_t*) buf)[7] = (x >> 0) & 0xff; +} + +_pure_ static uint64_t uint64_import(const void *buf, size_t buflen) { + assert(buflen == 8); + return + (uint64_t)(((uint8_t*) buf)[0]) << 56 | + (uint64_t)(((uint8_t*) buf)[1]) << 48 | + (uint64_t)(((uint8_t*) buf)[2]) << 40 | + (uint64_t)(((uint8_t*) buf)[3]) << 32 | + (uint64_t)(((uint8_t*) buf)[4]) << 24 | + (uint64_t)(((uint8_t*) buf)[5]) << 16 | + (uint64_t)(((uint8_t*) buf)[6]) << 8 | + (uint64_t)(((uint8_t*) buf)[7]) << 0; +} + +/* deterministically generate from seed/idx a string of buflen pseudorandom bytes */ +static void det_randomize(void *buf, size_t buflen, const void *seed, size_t seedlen, uint32_t idx) { + gcry_md_hd_t hd, hd2; + size_t olen, cpylen; + uint32_t ctr; + + olen = gcry_md_get_algo_dlen(RND_HASH); + gcry_md_open(&hd, RND_HASH, 0); + gcry_md_write(hd, seed, seedlen); + gcry_md_putc(hd, (idx >> 24) & 0xff); + gcry_md_putc(hd, (idx >> 16) & 0xff); + gcry_md_putc(hd, (idx >> 8) & 0xff); + gcry_md_putc(hd, (idx >> 0) & 0xff); + + for (ctr = 0; buflen; ctr++) { + gcry_md_copy(&hd2, hd); + gcry_md_putc(hd2, (ctr >> 24) & 0xff); + gcry_md_putc(hd2, (ctr >> 16) & 0xff); + gcry_md_putc(hd2, (ctr >> 8) & 0xff); + gcry_md_putc(hd2, (ctr >> 0) & 0xff); + gcry_md_final(hd2); + cpylen = (buflen < olen) ? buflen : olen; + memcpy(buf, gcry_md_read(hd2, RND_HASH), cpylen); + gcry_md_close(hd2); + buf += cpylen; + buflen -= cpylen; + } + gcry_md_close(hd); +} + +/* deterministically generate from seed/idx a prime of length `bits' that is 3 (mod 4) */ +static gcry_mpi_t genprime3mod4(int bits, const void *seed, size_t seedlen, uint32_t idx) { + size_t buflen = bits / 8; + uint8_t buf[buflen]; + gcry_mpi_t p; + + assert(bits % 8 == 0); + assert(buflen > 0); + + det_randomize(buf, buflen, seed, seedlen, idx); + buf[0] |= 0xc0; /* set upper two bits, so that n=pq has maximum size */ + buf[buflen - 1] |= 0x03; /* set lower two bits, to have result 3 (mod 4) */ + + p = mpi_import(buf, buflen); + while (gcry_prime_check(p, 0)) + gcry_mpi_add_ui(p, p, 4); + + return p; +} + +/* deterministically generate from seed/idx a quadratic residue (mod n) */ +static gcry_mpi_t gensquare(const gcry_mpi_t n, const void *seed, size_t seedlen, uint32_t idx, unsigned secpar) { + size_t buflen = secpar / 8; + uint8_t buf[buflen]; + gcry_mpi_t x; + + det_randomize(buf, buflen, seed, seedlen, idx); + buf[0] &= 0x7f; /* clear upper bit, so that we have x < n */ + x = mpi_import(buf, buflen); + assert(gcry_mpi_cmp(x, n) < 0); + gcry_mpi_mulm(x, x, x, n); + return x; +} + +/* compute 2^m (mod phi(p)), for a prime p */ +static gcry_mpi_t twopowmodphi(uint64_t m, const gcry_mpi_t p) { + gcry_mpi_t phi, r; + int n; + + phi = gcry_mpi_new(0); + gcry_mpi_sub_ui(phi, p, 1); + + /* count number of used bits in m */ + for (n = 0; (1ULL << n) <= m; n++) + ; + + r = gcry_mpi_new(0); + gcry_mpi_set_ui(r, 1); + while (n) { /* square and multiply algorithm for fast exponentiation */ + n--; + gcry_mpi_mulm(r, r, r, phi); + if (m & ((uint64_t)1 << n)) { + gcry_mpi_add(r, r, r); + if (gcry_mpi_cmp(r, phi) >= 0) + gcry_mpi_sub(r, r, phi); + } + } + + gcry_mpi_release(phi); + return r; +} + +/* Decompose $x \in Z_n$ into $(xp,xq) \in Z_p \times Z_q$ using Chinese Remainder Theorem */ +static void CRT_decompose(gcry_mpi_t *xp, gcry_mpi_t *xq, const gcry_mpi_t x, const gcry_mpi_t p, const gcry_mpi_t q) { + *xp = gcry_mpi_new(0); + *xq = gcry_mpi_new(0); + gcry_mpi_mod(*xp, x, p); + gcry_mpi_mod(*xq, x, q); +} + +/* Compose $(xp,xq) \in Z_p \times Z_q$ into $x \in Z_n$ using Chinese Remainder Theorem */ +static void CRT_compose(gcry_mpi_t *x, const gcry_mpi_t xp, const gcry_mpi_t xq, const gcry_mpi_t p, const gcry_mpi_t q) { + gcry_mpi_t a, u; + + a = gcry_mpi_new(0); + u = gcry_mpi_new(0); + *x = gcry_mpi_new(0); + gcry_mpi_subm(a, xq, xp, q); + gcry_mpi_invm(u, p, q); + gcry_mpi_mulm(a, a, u, q); /* a = (xq - xp) / p (mod q) */ + gcry_mpi_mul(*x, p, a); + gcry_mpi_add(*x, *x, xp); /* x = p * ((xq - xp) / p mod q) + xp */ + gcry_mpi_release(a); + gcry_mpi_release(u); +} + +/******************************************************************************/ + +size_t FSPRG_mskinbytes(unsigned _secpar) { + VALIDATE_SECPAR(_secpar); + return 2 + 2 * (_secpar / 2) / 8; /* to store header,p,q */ +} + +size_t FSPRG_mpkinbytes(unsigned _secpar) { + VALIDATE_SECPAR(_secpar); + return 2 + _secpar / 8; /* to store header,n */ +} + +size_t FSPRG_stateinbytes(unsigned _secpar) { + VALIDATE_SECPAR(_secpar); + return 2 + 2 * _secpar / 8 + 8; /* to store header,n,x,epoch */ +} + +static void store_secpar(void *buf, uint16_t secpar) { + secpar = secpar / 16 - 1; + ((uint8_t*) buf)[0] = (secpar >> 8) & 0xff; + ((uint8_t*) buf)[1] = (secpar >> 0) & 0xff; +} + +static uint16_t read_secpar(const void *buf) { + uint16_t secpar; + secpar = + (uint16_t)(((uint8_t*) buf)[0]) << 8 | + (uint16_t)(((uint8_t*) buf)[1]) << 0; + return 16 * (secpar + 1); +} + +void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned _secpar) { + uint8_t iseed[FSPRG_RECOMMENDED_SEEDLEN]; + gcry_mpi_t n, p, q; + uint16_t secpar; + + VALIDATE_SECPAR(_secpar); + secpar = _secpar; + + initialize_libgcrypt(false); + + if (!seed) { + gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM); + seed = iseed; + seedlen = FSPRG_RECOMMENDED_SEEDLEN; + } + + p = genprime3mod4(secpar / 2, seed, seedlen, RND_GEN_P); + q = genprime3mod4(secpar / 2, seed, seedlen, RND_GEN_Q); + + if (msk) { + store_secpar(msk + 0, secpar); + mpi_export(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8, p); + mpi_export(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8, q); + } + + if (mpk) { + n = gcry_mpi_new(0); + gcry_mpi_mul(n, p, q); + assert(gcry_mpi_get_nbits(n) == secpar); + + store_secpar(mpk + 0, secpar); + mpi_export(mpk + 2, secpar / 8, n); + + gcry_mpi_release(n); + } + + gcry_mpi_release(p); + gcry_mpi_release(q); +} + +void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen) { + gcry_mpi_t n, x; + uint16_t secpar; + + initialize_libgcrypt(false); + + secpar = read_secpar(mpk + 0); + n = mpi_import(mpk + 2, secpar / 8); + x = gensquare(n, seed, seedlen, RND_GEN_X, secpar); + + memcpy(state, mpk, 2 + secpar / 8); + mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x); + memzero(state + 2 + 2 * secpar / 8, 8); + + gcry_mpi_release(n); + gcry_mpi_release(x); +} + +void FSPRG_Evolve(void *state) { + gcry_mpi_t n, x; + uint16_t secpar; + uint64_t epoch; + + initialize_libgcrypt(false); + + secpar = read_secpar(state + 0); + n = mpi_import(state + 2 + 0 * secpar / 8, secpar / 8); + x = mpi_import(state + 2 + 1 * secpar / 8, secpar / 8); + epoch = uint64_import(state + 2 + 2 * secpar / 8, 8); + + gcry_mpi_mulm(x, x, x, n); + epoch++; + + mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x); + uint64_export(state + 2 + 2 * secpar / 8, 8, epoch); + + gcry_mpi_release(n); + gcry_mpi_release(x); +} + +uint64_t FSPRG_GetEpoch(const void *state) { + uint16_t secpar; + secpar = read_secpar(state + 0); + return uint64_import(state + 2 + 2 * secpar / 8, 8); +} + +void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen) { + gcry_mpi_t p, q, n, x, xp, xq, kp, kq, xm; + uint16_t secpar; + + initialize_libgcrypt(false); + + secpar = read_secpar(msk + 0); + p = mpi_import(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8); + q = mpi_import(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8); + + n = gcry_mpi_new(0); + gcry_mpi_mul(n, p, q); + + x = gensquare(n, seed, seedlen, RND_GEN_X, secpar); + CRT_decompose(&xp, &xq, x, p, q); /* split (mod n) into (mod p) and (mod q) using CRT */ + + kp = twopowmodphi(epoch, p); /* compute 2^epoch (mod phi(p)) */ + kq = twopowmodphi(epoch, q); /* compute 2^epoch (mod phi(q)) */ + + gcry_mpi_powm(xp, xp, kp, p); /* compute x^(2^epoch) (mod p) */ + gcry_mpi_powm(xq, xq, kq, q); /* compute x^(2^epoch) (mod q) */ + + CRT_compose(&xm, xp, xq, p, q); /* combine (mod p) and (mod q) to (mod n) using CRT */ + + store_secpar(state + 0, secpar); + mpi_export(state + 2 + 0 * secpar / 8, secpar / 8, n); + mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, xm); + uint64_export(state + 2 + 2 * secpar / 8, 8, epoch); + + gcry_mpi_release(p); + gcry_mpi_release(q); + gcry_mpi_release(n); + gcry_mpi_release(x); + gcry_mpi_release(xp); + gcry_mpi_release(xq); + gcry_mpi_release(kp); + gcry_mpi_release(kq); + gcry_mpi_release(xm); +} + +void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) { + uint16_t secpar; + + initialize_libgcrypt(false); + + secpar = read_secpar(state + 0); + det_randomize(key, keylen, state + 2, 2 * secpar / 8 + 8, idx); +} diff --git a/src/libsystemd/src/sd-journal/fsprg.h b/src/libsystemd/src/sd-journal/fsprg.h new file mode 100644 index 0000000000..829b56e240 --- /dev/null +++ b/src/libsystemd/src/sd-journal/fsprg.h @@ -0,0 +1,65 @@ +#ifndef __fsprgh__ +#define __fsprgh__ + +/* + * fsprg v0.1 - (seekable) forward-secure pseudorandom generator + * Copyright (C) 2012 B. Poettering + * Contact: fsprg@point-at-infinity.org + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include + +#include "macro.h" +#include "util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FSPRG_RECOMMENDED_SECPAR 1536 +#define FSPRG_RECOMMENDED_SEEDLEN (96/8) + +size_t FSPRG_mskinbytes(unsigned secpar) _const_; +size_t FSPRG_mpkinbytes(unsigned secpar) _const_; +size_t FSPRG_stateinbytes(unsigned secpar) _const_; + +/* Setup msk and mpk. Providing seed != NULL makes this algorithm deterministic. */ +void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned secpar); + +/* Initialize state deterministically in dependence on seed. */ +/* Note: in case one wants to run only one GenState0 per GenMK it is safe to use + the same seed for both GenMK and GenState0. +*/ +void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen); + +void FSPRG_Evolve(void *state); + +uint64_t FSPRG_GetEpoch(const void *state) _pure_; + +/* Seek to any arbitrary state (by providing msk together with seed from GenState0). */ +void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen); + +void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libsystemd/src/sd-journal/journal-authenticate.c b/src/libsystemd/src/sd-journal/journal-authenticate.c new file mode 100644 index 0000000000..d8af113d3f --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-authenticate.c @@ -0,0 +1,551 @@ +/*** + This file is part of systemd. + + Copyright 2012 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 . +***/ + +#include +#include + +#include "fd-util.h" +#include "fsprg.h" +#include "gcrypt-util.h" +#include "hexdecoct.h" +#include "journal-authenticate.h" +#include "journal-def.h" +#include "journal-file.h" + +static uint64_t journal_file_tag_seqnum(JournalFile *f) { + uint64_t r; + + assert(f); + + r = le64toh(f->header->n_tags) + 1; + f->header->n_tags = htole64(r); + + return r; +} + +int journal_file_append_tag(JournalFile *f) { + Object *o; + uint64_t p; + int r; + + assert(f); + + if (!f->seal) + return 0; + + if (!f->hmac_running) + return 0; + + assert(f->hmac); + + r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p); + if (r < 0) + return r; + + o->tag.seqnum = htole64(journal_file_tag_seqnum(f)); + o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state)); + + log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"", + le64toh(o->tag.seqnum), + FSPRG_GetEpoch(f->fsprg_state)); + + /* Add the tag object itself, so that we can protect its + * header. This will exclude the actual hash value in it */ + r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p); + if (r < 0) + return r; + + /* Get the HMAC tag and store it in the object */ + memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH); + f->hmac_running = false; + + return 0; +} + +int journal_file_hmac_start(JournalFile *f) { + uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */ + assert(f); + + if (!f->seal) + return 0; + + if (f->hmac_running) + return 0; + + /* Prepare HMAC for next cycle */ + gcry_md_reset(f->hmac); + FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0); + gcry_md_setkey(f->hmac, key, sizeof(key)); + + f->hmac_running = true; + + return 0; +} + +static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) { + uint64_t t; + + assert(f); + assert(epoch); + assert(f->seal); + + if (f->fss_start_usec == 0 || + f->fss_interval_usec == 0) + return -EOPNOTSUPP; + + if (realtime < f->fss_start_usec) + return -ESTALE; + + t = realtime - f->fss_start_usec; + t = t / f->fss_interval_usec; + + *epoch = t; + return 0; +} + +static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) { + uint64_t goal, epoch; + int r; + assert(f); + + if (!f->seal) + return 0; + + r = journal_file_get_epoch(f, realtime, &goal); + if (r < 0) + return r; + + epoch = FSPRG_GetEpoch(f->fsprg_state); + if (epoch > goal) + return -ESTALE; + + return epoch != goal; +} + +int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) { + uint64_t goal, epoch; + int r; + + assert(f); + + if (!f->seal) + return 0; + + r = journal_file_get_epoch(f, realtime, &goal); + if (r < 0) + return r; + + epoch = FSPRG_GetEpoch(f->fsprg_state); + if (epoch < goal) + log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal); + + for (;;) { + if (epoch > goal) + return -ESTALE; + if (epoch == goal) + return 0; + + FSPRG_Evolve(f->fsprg_state); + epoch = FSPRG_GetEpoch(f->fsprg_state); + } +} + +int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) { + void *msk; + uint64_t epoch; + + assert(f); + + if (!f->seal) + return 0; + + assert(f->fsprg_seed); + + if (f->fsprg_state) { + /* Cheaper... */ + + epoch = FSPRG_GetEpoch(f->fsprg_state); + if (goal == epoch) + return 0; + + if (goal == epoch+1) { + FSPRG_Evolve(f->fsprg_state); + return 0; + } + } else { + f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR); + f->fsprg_state = malloc(f->fsprg_state_size); + + if (!f->fsprg_state) + return -ENOMEM; + } + + log_debug("Seeking FSPRG key to %"PRIu64".", goal); + + msk = alloca(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR)); + FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR); + FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size); + return 0; +} + +int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { + int r; + + assert(f); + + if (!f->seal) + return 0; + + if (realtime <= 0) + realtime = now(CLOCK_REALTIME); + + r = journal_file_fsprg_need_evolve(f, realtime); + if (r <= 0) + return 0; + + r = journal_file_append_tag(f); + if (r < 0) + return r; + + r = journal_file_fsprg_evolve(f, realtime); + if (r < 0) + return r; + + return 0; +} + +int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) { + int r; + + assert(f); + + if (!f->seal) + return 0; + + r = journal_file_hmac_start(f); + if (r < 0) + return r; + + if (!o) { + r = journal_file_move_to_object(f, type, p, &o); + if (r < 0) + return r; + } else { + if (type > OBJECT_UNUSED && o->object.type != type) + return -EBADMSG; + } + + gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload)); + + switch (o->object.type) { + + case OBJECT_DATA: + /* All but hash and payload are mutable */ + gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash)); + gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload)); + break; + + case OBJECT_FIELD: + /* Same here */ + gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash)); + gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(FieldObject, payload)); + break; + + case OBJECT_ENTRY: + /* All */ + gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum)); + break; + + case OBJECT_FIELD_HASH_TABLE: + case OBJECT_DATA_HASH_TABLE: + case OBJECT_ENTRY_ARRAY: + /* Nothing: everything is mutable */ + break; + + case OBJECT_TAG: + /* All but the tag itself */ + gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum)); + gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch)); + break; + default: + return -EINVAL; + } + + return 0; +} + +int journal_file_hmac_put_header(JournalFile *f) { + int r; + + assert(f); + + if (!f->seal) + return 0; + + r = journal_file_hmac_start(f); + if (r < 0) + return r; + + /* All but state+reserved, boot_id, arena_size, + * tail_object_offset, n_objects, n_entries, + * tail_entry_seqnum, head_entry_seqnum, entry_array_offset, + * head_entry_realtime, tail_entry_realtime, + * tail_entry_monotonic, n_data, n_fields, n_tags, + * n_entry_arrays. */ + + gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature)); + gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id)); + gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id)); + gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset)); + + return 0; +} + +int journal_file_fss_load(JournalFile *f) { + int r, fd = -1; + char *p = NULL; + struct stat st; + FSSHeader *m = NULL; + sd_id128_t machine; + + assert(f); + + if (!f->seal) + return 0; + + r = sd_id128_get_machine(&machine); + if (r < 0) + return r; + + if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss", + SD_ID128_FORMAT_VAL(machine)) < 0) + return -ENOMEM; + + fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600); + if (fd < 0) { + if (errno != ENOENT) + log_error_errno(errno, "Failed to open %s: %m", p); + + r = -errno; + goto finish; + } + + if (fstat(fd, &st) < 0) { + r = -errno; + goto finish; + } + + if (st.st_size < (off_t) sizeof(FSSHeader)) { + r = -ENODATA; + goto finish; + } + + m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0); + if (m == MAP_FAILED) { + m = NULL; + r = -errno; + goto finish; + } + + if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) { + r = -EBADMSG; + goto finish; + } + + if (m->incompatible_flags != 0) { + r = -EPROTONOSUPPORT; + goto finish; + } + + if (le64toh(m->header_size) < sizeof(FSSHeader)) { + r = -EBADMSG; + goto finish; + } + + if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) { + r = -EBADMSG; + goto finish; + } + + f->fss_file_size = le64toh(m->header_size) + le64toh(m->fsprg_state_size); + if ((uint64_t) st.st_size < f->fss_file_size) { + r = -ENODATA; + goto finish; + } + + if (!sd_id128_equal(machine, m->machine_id)) { + r = -EHOSTDOWN; + goto finish; + } + + if (le64toh(m->start_usec) <= 0 || + le64toh(m->interval_usec) <= 0) { + r = -EBADMSG; + goto finish; + } + + f->fss_file = mmap(NULL, PAGE_ALIGN(f->fss_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (f->fss_file == MAP_FAILED) { + f->fss_file = NULL; + r = -errno; + goto finish; + } + + f->fss_start_usec = le64toh(f->fss_file->start_usec); + f->fss_interval_usec = le64toh(f->fss_file->interval_usec); + + f->fsprg_state = (uint8_t*) f->fss_file + le64toh(f->fss_file->header_size); + f->fsprg_state_size = le64toh(f->fss_file->fsprg_state_size); + + r = 0; + +finish: + if (m) + munmap(m, PAGE_ALIGN(sizeof(FSSHeader))); + + safe_close(fd); + free(p); + + return r; +} + +int journal_file_hmac_setup(JournalFile *f) { + gcry_error_t e; + + if (!f->seal) + return 0; + + initialize_libgcrypt(true); + + e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); + if (e != 0) + return -EOPNOTSUPP; + + return 0; +} + +int journal_file_append_first_tag(JournalFile *f) { + int r; + uint64_t p; + + if (!f->seal) + return 0; + + log_debug("Calculating first tag..."); + + r = journal_file_hmac_put_header(f); + if (r < 0) + return r; + + p = le64toh(f->header->field_hash_table_offset); + if (p < offsetof(Object, hash_table.items)) + return -EINVAL; + p -= offsetof(Object, hash_table.items); + + r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p); + if (r < 0) + return r; + + p = le64toh(f->header->data_hash_table_offset); + if (p < offsetof(Object, hash_table.items)) + return -EINVAL; + p -= offsetof(Object, hash_table.items); + + r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p); + if (r < 0) + return r; + + r = journal_file_append_tag(f); + if (r < 0) + return r; + + return 0; +} + +int journal_file_parse_verification_key(JournalFile *f, const char *key) { + uint8_t *seed; + size_t seed_size, c; + const char *k; + int r; + unsigned long long start, interval; + + seed_size = FSPRG_RECOMMENDED_SEEDLEN; + seed = malloc(seed_size); + if (!seed) + return -ENOMEM; + + k = key; + for (c = 0; c < seed_size; c++) { + int x, y; + + while (*k == '-') + k++; + + x = unhexchar(*k); + if (x < 0) { + free(seed); + return -EINVAL; + } + k++; + y = unhexchar(*k); + if (y < 0) { + free(seed); + return -EINVAL; + } + k++; + + seed[c] = (uint8_t) (x * 16 + y); + } + + if (*k != '/') { + free(seed); + return -EINVAL; + } + k++; + + r = sscanf(k, "%llx-%llx", &start, &interval); + if (r != 2) { + free(seed); + return -EINVAL; + } + + f->fsprg_seed = seed; + f->fsprg_seed_size = seed_size; + + f->fss_start_usec = start * interval; + f->fss_interval_usec = interval; + + return 0; +} + +bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) { + uint64_t epoch; + + assert(f); + assert(u); + + if (!f->seal) + return false; + + epoch = FSPRG_GetEpoch(f->fsprg_state); + + *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec); + + return true; +} diff --git a/src/libsystemd/src/sd-journal/journal-authenticate.h b/src/libsystemd/src/sd-journal/journal-authenticate.h new file mode 100644 index 0000000000..6c87319ede --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-authenticate.h @@ -0,0 +1,41 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 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 . +***/ + +#include + +#include "journal-file.h" + +int journal_file_append_tag(JournalFile *f); +int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime); +int journal_file_append_first_tag(JournalFile *f); + +int journal_file_hmac_setup(JournalFile *f); +int journal_file_hmac_start(JournalFile *f); +int journal_file_hmac_put_header(JournalFile *f); +int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p); + +int journal_file_fss_load(JournalFile *f); +int journal_file_parse_verification_key(JournalFile *f, const char *key); + +int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime); +int journal_file_fsprg_seek(JournalFile *f, uint64_t epoch); + +bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u); diff --git a/src/libsystemd/src/sd-journal/journal-def.h b/src/libsystemd/src/sd-journal/journal-def.h new file mode 100644 index 0000000000..a0a052ce4f --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-def.h @@ -0,0 +1,237 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include + +#include "macro.h" +#include "sparse-endian.h" + +/* + * If you change this file you probably should also change its documentation: + * + * http://www.freedesktop.org/wiki/Software/systemd/journal-files + * + */ + +typedef struct Header Header; + +typedef struct ObjectHeader ObjectHeader; +typedef union Object Object; + +typedef struct DataObject DataObject; +typedef struct FieldObject FieldObject; +typedef struct EntryObject EntryObject; +typedef struct HashTableObject HashTableObject; +typedef struct EntryArrayObject EntryArrayObject; +typedef struct TagObject TagObject; + +typedef struct EntryItem EntryItem; +typedef struct HashItem HashItem; + +typedef struct FSSHeader FSSHeader; + +/* Object types */ +typedef enum ObjectType { + OBJECT_UNUSED, /* also serves as "any type" or "additional context" */ + OBJECT_DATA, + OBJECT_FIELD, + OBJECT_ENTRY, + OBJECT_DATA_HASH_TABLE, + OBJECT_FIELD_HASH_TABLE, + OBJECT_ENTRY_ARRAY, + OBJECT_TAG, + _OBJECT_TYPE_MAX +} ObjectType; + +/* Object flags */ +enum { + OBJECT_COMPRESSED_XZ = 1 << 0, + OBJECT_COMPRESSED_LZ4 = 1 << 1, + _OBJECT_COMPRESSED_MAX +}; + +#define OBJECT_COMPRESSION_MASK (OBJECT_COMPRESSED_XZ | OBJECT_COMPRESSED_LZ4) + +struct ObjectHeader { + uint8_t type; + uint8_t flags; + uint8_t reserved[6]; + le64_t size; + uint8_t payload[]; +} _packed_; + +struct DataObject { + ObjectHeader object; + le64_t hash; + le64_t next_hash_offset; + le64_t next_field_offset; + le64_t entry_offset; /* the first array entry we store inline */ + le64_t entry_array_offset; + le64_t n_entries; + uint8_t payload[]; +} _packed_; + +struct FieldObject { + ObjectHeader object; + le64_t hash; + le64_t next_hash_offset; + le64_t head_data_offset; + uint8_t payload[]; +} _packed_; + +struct EntryItem { + le64_t object_offset; + le64_t hash; +} _packed_; + +struct EntryObject { + ObjectHeader object; + le64_t seqnum; + le64_t realtime; + le64_t monotonic; + sd_id128_t boot_id; + le64_t xor_hash; + EntryItem items[]; +} _packed_; + +struct HashItem { + le64_t head_hash_offset; + le64_t tail_hash_offset; +} _packed_; + +struct HashTableObject { + ObjectHeader object; + HashItem items[]; +} _packed_; + +struct EntryArrayObject { + ObjectHeader object; + le64_t next_entry_array_offset; + le64_t items[]; +} _packed_; + +#define TAG_LENGTH (256/8) + +struct TagObject { + ObjectHeader object; + le64_t seqnum; + le64_t epoch; + uint8_t tag[TAG_LENGTH]; /* SHA-256 HMAC */ +} _packed_; + +union Object { + ObjectHeader object; + DataObject data; + FieldObject field; + EntryObject entry; + HashTableObject hash_table; + EntryArrayObject entry_array; + TagObject tag; +}; + +enum { + STATE_OFFLINE = 0, + STATE_ONLINE = 1, + STATE_ARCHIVED = 2, + _STATE_MAX +}; + +/* Header flags */ +enum { + HEADER_INCOMPATIBLE_COMPRESSED_XZ = 1 << 0, + HEADER_INCOMPATIBLE_COMPRESSED_LZ4 = 1 << 1, +}; + +#define HEADER_INCOMPATIBLE_ANY (HEADER_INCOMPATIBLE_COMPRESSED_XZ|HEADER_INCOMPATIBLE_COMPRESSED_LZ4) + +#if defined(HAVE_XZ) && defined(HAVE_LZ4) +# define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_ANY +#elif defined(HAVE_XZ) +# define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_COMPRESSED_XZ +#elif defined(HAVE_LZ4) +# define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_COMPRESSED_LZ4 +#else +# define HEADER_INCOMPATIBLE_SUPPORTED 0 +#endif + +enum { + HEADER_COMPATIBLE_SEALED = 1 +}; + +#define HEADER_COMPATIBLE_ANY HEADER_COMPATIBLE_SEALED +#ifdef HAVE_GCRYPT +# define HEADER_COMPATIBLE_SUPPORTED HEADER_COMPATIBLE_SEALED +#else +# define HEADER_COMPATIBLE_SUPPORTED 0 +#endif + +#define HEADER_SIGNATURE ((char[]) { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' }) + +struct Header { + uint8_t signature[8]; /* "LPKSHHRH" */ + le32_t compatible_flags; + le32_t incompatible_flags; + uint8_t state; + uint8_t reserved[7]; + sd_id128_t file_id; + sd_id128_t machine_id; + sd_id128_t boot_id; /* last writer */ + sd_id128_t seqnum_id; + le64_t header_size; + le64_t arena_size; + le64_t data_hash_table_offset; + le64_t data_hash_table_size; + le64_t field_hash_table_offset; + le64_t field_hash_table_size; + le64_t tail_object_offset; + le64_t n_objects; + le64_t n_entries; + le64_t tail_entry_seqnum; + le64_t head_entry_seqnum; + le64_t entry_array_offset; + le64_t head_entry_realtime; + le64_t tail_entry_realtime; + le64_t tail_entry_monotonic; + /* Added in 187 */ + le64_t n_data; + le64_t n_fields; + /* Added in 189 */ + le64_t n_tags; + le64_t n_entry_arrays; + + /* Size: 240 */ +} _packed_; + +#define FSS_HEADER_SIGNATURE ((char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' }) + +struct FSSHeader { + uint8_t signature[8]; /* "KSHHRHLP" */ + le32_t compatible_flags; + le32_t incompatible_flags; + sd_id128_t machine_id; + sd_id128_t boot_id; /* last writer */ + le64_t header_size; + le64_t start_usec; + le64_t interval_usec; + le16_t fsprg_secpar; + le16_t reserved[3]; + le64_t fsprg_state_size; +} _packed_; diff --git a/src/libsystemd/src/sd-journal/journal-file.c b/src/libsystemd/src/sd-journal/journal-file.c new file mode 100644 index 0000000000..13db5c53de --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-file.c @@ -0,0 +1,3616 @@ +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "alloc-util.h" +#include "btrfs-util.h" +#include "chattr-util.h" +#include "compress.h" +#include "fd-util.h" +#include "journal-authenticate.h" +#include "journal-def.h" +#include "journal-file.h" +#include "lookup3.h" +#include "parse-util.h" +#include "path-util.h" +#include "random-util.h" +#include +#include "set.h" +#include "string-util.h" +#include "xattr-util.h" + +#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem)) +#define DEFAULT_FIELD_HASH_TABLE_SIZE (333ULL*sizeof(HashItem)) + +#define COMPRESSION_SIZE_THRESHOLD (512ULL) + +/* This is the minimum journal file size */ +#define JOURNAL_FILE_SIZE_MIN (512ULL*1024ULL) /* 512 KiB */ + +/* These are the lower and upper bounds if we deduce the max_use value + * from the file system size */ +#define DEFAULT_MAX_USE_LOWER (1ULL*1024ULL*1024ULL) /* 1 MiB */ +#define DEFAULT_MAX_USE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ + +/* This is the default minimal use limit, how much we'll use even if keep_free suggests otherwise. */ +#define DEFAULT_MIN_USE (1ULL*1024ULL*1024ULL) /* 1 MiB */ + +/* This is the upper bound if we deduce max_size from max_use */ +#define DEFAULT_MAX_SIZE_UPPER (128ULL*1024ULL*1024ULL) /* 128 MiB */ + +/* This is the upper bound if we deduce the keep_free value from the + * file system size */ +#define DEFAULT_KEEP_FREE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ + +/* This is the keep_free value when we can't determine the system + * size */ +#define DEFAULT_KEEP_FREE (1024ULL*1024ULL) /* 1 MB */ + +/* This is the default maximum number of journal files to keep around. */ +#define DEFAULT_N_MAX_FILES (100) + +/* n_data was the first entry we added after the initial file format design */ +#define HEADER_SIZE_MIN ALIGN64(offsetof(Header, n_data)) + +/* How many entries to keep in the entry array chain cache at max */ +#define CHAIN_CACHE_MAX 20 + +/* How much to increase the journal file size at once each time we allocate something new. */ +#define FILE_SIZE_INCREASE (8ULL*1024ULL*1024ULL) /* 8MB */ + +/* Reread fstat() of the file for detecting deletions at least this often */ +#define LAST_STAT_REFRESH_USEC (5*USEC_PER_SEC) + +/* The mmap context to use for the header we pick as one above the last defined typed */ +#define CONTEXT_HEADER _OBJECT_TYPE_MAX + +/* This may be called from a separate thread to prevent blocking the caller for the duration of fsync(). + * As a result we use atomic operations on f->offline_state for inter-thread communications with + * journal_file_set_offline() and journal_file_set_online(). */ +static void journal_file_set_offline_internal(JournalFile *f) { + assert(f); + assert(f->fd >= 0); + assert(f->header); + + for (;;) { + switch (f->offline_state) { + case OFFLINE_CANCEL: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_CANCEL, OFFLINE_DONE)) + continue; + return; + + case OFFLINE_AGAIN_FROM_SYNCING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_SYNCING)) + continue; + break; + + case OFFLINE_AGAIN_FROM_OFFLINING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_SYNCING)) + continue; + break; + + case OFFLINE_SYNCING: + (void) fsync(f->fd); + + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_OFFLINING)) + continue; + + f->header->state = f->archive ? STATE_ARCHIVED : STATE_OFFLINE; + (void) fsync(f->fd); + break; + + case OFFLINE_OFFLINING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_OFFLINING, OFFLINE_DONE)) + continue; + /* fall through */ + + case OFFLINE_DONE: + return; + + case OFFLINE_JOINED: + log_debug("OFFLINE_JOINED unexpected offline state for journal_file_set_offline_internal()"); + return; + } + } +} + +static void * journal_file_set_offline_thread(void *arg) { + JournalFile *f = arg; + + journal_file_set_offline_internal(f); + + return NULL; +} + +static int journal_file_set_offline_thread_join(JournalFile *f) { + int r; + + assert(f); + + if (f->offline_state == OFFLINE_JOINED) + return 0; + + r = pthread_join(f->offline_thread, NULL); + if (r) + return -r; + + f->offline_state = OFFLINE_JOINED; + + if (mmap_cache_got_sigbus(f->mmap, f->fd)) + return -EIO; + + return 0; +} + +/* Trigger a restart if the offline thread is mid-flight in a restartable state. */ +static bool journal_file_set_offline_try_restart(JournalFile *f) { + for (;;) { + switch (f->offline_state) { + case OFFLINE_AGAIN_FROM_SYNCING: + case OFFLINE_AGAIN_FROM_OFFLINING: + return true; + + case OFFLINE_CANCEL: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_CANCEL, OFFLINE_AGAIN_FROM_SYNCING)) + continue; + return true; + + case OFFLINE_SYNCING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_AGAIN_FROM_SYNCING)) + continue; + return true; + + case OFFLINE_OFFLINING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_OFFLINING, OFFLINE_AGAIN_FROM_OFFLINING)) + continue; + return true; + + default: + return false; + } + } +} + +/* Sets a journal offline. + * + * If wait is false then an offline is dispatched in a separate thread for a + * subsequent journal_file_set_offline() or journal_file_set_online() of the + * same journal to synchronize with. + * + * If wait is true, then either an existing offline thread will be restarted + * and joined, or if none exists the offline is simply performed in this + * context without involving another thread. + */ +int journal_file_set_offline(JournalFile *f, bool wait) { + bool restarted; + int r; + + assert(f); + + if (!f->writable) + return -EPERM; + + if (!(f->fd >= 0 && f->header)) + return -EINVAL; + + /* An offlining journal is implicitly online and may modify f->header->state, + * we must also join any potentially lingering offline thread when not online. */ + if (!journal_file_is_offlining(f) && f->header->state != STATE_ONLINE) + return journal_file_set_offline_thread_join(f); + + /* Restart an in-flight offline thread and wait if needed, or join a lingering done one. */ + restarted = journal_file_set_offline_try_restart(f); + if ((restarted && wait) || !restarted) { + r = journal_file_set_offline_thread_join(f); + if (r < 0) + return r; + } + + if (restarted) + return 0; + + /* Initiate a new offline. */ + f->offline_state = OFFLINE_SYNCING; + + if (wait) /* Without using a thread if waiting. */ + journal_file_set_offline_internal(f); + else { + r = pthread_create(&f->offline_thread, NULL, journal_file_set_offline_thread, f); + if (r > 0) { + f->offline_state = OFFLINE_JOINED; + return -r; + } + } + + return 0; +} + +static int journal_file_set_online(JournalFile *f) { + bool joined = false; + + assert(f); + + if (!f->writable) + return -EPERM; + + if (!(f->fd >= 0 && f->header)) + return -EINVAL; + + while (!joined) { + switch (f->offline_state) { + case OFFLINE_JOINED: + /* No offline thread, no need to wait. */ + joined = true; + break; + + case OFFLINE_SYNCING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_CANCEL)) + continue; + /* Canceled syncing prior to offlining, no need to wait. */ + break; + + case OFFLINE_AGAIN_FROM_SYNCING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_CANCEL)) + continue; + /* Canceled restart from syncing, no need to wait. */ + break; + + case OFFLINE_AGAIN_FROM_OFFLINING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_CANCEL)) + continue; + /* Canceled restart from offlining, must wait for offlining to complete however. */ + + /* fall through to wait */ + default: { + int r; + + r = journal_file_set_offline_thread_join(f); + if (r < 0) + return r; + + joined = true; + break; + } + } + } + + if (mmap_cache_got_sigbus(f->mmap, f->fd)) + return -EIO; + + switch (f->header->state) { + case STATE_ONLINE: + return 0; + + case STATE_OFFLINE: + f->header->state = STATE_ONLINE; + (void) fsync(f->fd); + return 0; + + default: + return -EINVAL; + } +} + +bool journal_file_is_offlining(JournalFile *f) { + assert(f); + + __sync_synchronize(); + + if (f->offline_state == OFFLINE_DONE || + f->offline_state == OFFLINE_JOINED) + return false; + + return true; +} + +JournalFile* journal_file_close(JournalFile *f) { + assert(f); + +#ifdef HAVE_GCRYPT + /* Write the final tag */ + if (f->seal && f->writable) + journal_file_append_tag(f); +#endif + + if (f->post_change_timer) { + int enabled; + + if (sd_event_source_get_enabled(f->post_change_timer, &enabled) >= 0) + if (enabled == SD_EVENT_ONESHOT) + journal_file_post_change(f); + + (void) sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_OFF); + sd_event_source_unref(f->post_change_timer); + } + + journal_file_set_offline(f, true); + + if (f->mmap && f->fd >= 0) + mmap_cache_close_fd(f->mmap, f->fd); + + if (f->fd >= 0 && f->defrag_on_close) { + + /* Be friendly to btrfs: turn COW back on again now, + * and defragment the file. We won't write to the file + * ever again, hence remove all fragmentation, and + * reenable all the good bits COW usually provides + * (such as data checksumming). */ + + (void) chattr_fd(f->fd, 0, FS_NOCOW_FL); + (void) btrfs_defrag_fd(f->fd); + } + + if (f->close_fd) + safe_close(f->fd); + free(f->path); + + mmap_cache_unref(f->mmap); + + ordered_hashmap_free_free(f->chain_cache); + +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + free(f->compress_buffer); +#endif + +#ifdef HAVE_GCRYPT + if (f->fss_file) + munmap(f->fss_file, PAGE_ALIGN(f->fss_file_size)); + else + free(f->fsprg_state); + + free(f->fsprg_seed); + + if (f->hmac) + gcry_md_close(f->hmac); +#endif + + free(f); + return NULL; +} + +void journal_file_close_set(Set *s) { + JournalFile *f; + + assert(s); + + while ((f = set_steal_first(s))) + (void) journal_file_close(f); +} + +static int journal_file_init_header(JournalFile *f, JournalFile *template) { + Header h = {}; + ssize_t k; + int r; + + assert(f); + + memcpy(h.signature, HEADER_SIGNATURE, 8); + h.header_size = htole64(ALIGN64(sizeof(h))); + + h.incompatible_flags |= htole32( + f->compress_xz * HEADER_INCOMPATIBLE_COMPRESSED_XZ | + f->compress_lz4 * HEADER_INCOMPATIBLE_COMPRESSED_LZ4); + + h.compatible_flags = htole32( + f->seal * HEADER_COMPATIBLE_SEALED); + + r = sd_id128_randomize(&h.file_id); + if (r < 0) + return r; + + if (template) { + h.seqnum_id = template->header->seqnum_id; + h.tail_entry_seqnum = template->header->tail_entry_seqnum; + } else + h.seqnum_id = h.file_id; + + k = pwrite(f->fd, &h, sizeof(h), 0); + if (k < 0) + return -errno; + + if (k != sizeof(h)) + return -EIO; + + return 0; +} + +static int fsync_directory_of_file(int fd) { + _cleanup_free_ char *path = NULL, *dn = NULL; + _cleanup_close_ int dfd = -1; + struct stat st; + int r; + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISREG(st.st_mode)) + return -EBADFD; + + r = fd_get_path(fd, &path); + if (r < 0) + return r; + + if (!path_is_absolute(path)) + return -EINVAL; + + dn = dirname_malloc(path); + if (!dn) + return -ENOMEM; + + dfd = open(dn, O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (dfd < 0) + return -errno; + + if (fsync(dfd) < 0) + return -errno; + + return 0; +} + +static int journal_file_refresh_header(JournalFile *f) { + sd_id128_t boot_id; + int r; + + assert(f); + assert(f->header); + + r = sd_id128_get_machine(&f->header->machine_id); + if (r < 0) + return r; + + r = sd_id128_get_boot(&boot_id); + if (r < 0) + return r; + + if (sd_id128_equal(boot_id, f->header->boot_id)) + f->tail_entry_monotonic_valid = true; + + f->header->boot_id = boot_id; + + r = journal_file_set_online(f); + + /* Sync the online state to disk */ + (void) fsync(f->fd); + + /* We likely just created a new file, also sync the directory this file is located in. */ + (void) fsync_directory_of_file(f->fd); + + return r; +} + +static int journal_file_verify_header(JournalFile *f) { + uint32_t flags; + + assert(f); + assert(f->header); + + if (memcmp(f->header->signature, HEADER_SIGNATURE, 8)) + return -EBADMSG; + + /* In both read and write mode we refuse to open files with + * incompatible flags we don't know */ + flags = le32toh(f->header->incompatible_flags); + if (flags & ~HEADER_INCOMPATIBLE_SUPPORTED) { + if (flags & ~HEADER_INCOMPATIBLE_ANY) + log_debug("Journal file %s has unknown incompatible flags %"PRIx32, + f->path, flags & ~HEADER_INCOMPATIBLE_ANY); + flags = (flags & HEADER_INCOMPATIBLE_ANY) & ~HEADER_INCOMPATIBLE_SUPPORTED; + if (flags) + log_debug("Journal file %s uses incompatible flags %"PRIx32 + " disabled at compilation time.", f->path, flags); + return -EPROTONOSUPPORT; + } + + /* When open for writing we refuse to open files with + * compatible flags, too */ + flags = le32toh(f->header->compatible_flags); + if (f->writable && (flags & ~HEADER_COMPATIBLE_SUPPORTED)) { + if (flags & ~HEADER_COMPATIBLE_ANY) + log_debug("Journal file %s has unknown compatible flags %"PRIx32, + f->path, flags & ~HEADER_COMPATIBLE_ANY); + flags = (flags & HEADER_COMPATIBLE_ANY) & ~HEADER_COMPATIBLE_SUPPORTED; + if (flags) + log_debug("Journal file %s uses compatible flags %"PRIx32 + " disabled at compilation time.", f->path, flags); + return -EPROTONOSUPPORT; + } + + if (f->header->state >= _STATE_MAX) + return -EBADMSG; + + /* The first addition was n_data, so check that we are at least this large */ + if (le64toh(f->header->header_size) < HEADER_SIZE_MIN) + return -EBADMSG; + + if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) + return -EBADMSG; + + if ((le64toh(f->header->header_size) + le64toh(f->header->arena_size)) > (uint64_t) f->last_stat.st_size) + return -ENODATA; + + if (le64toh(f->header->tail_object_offset) > (le64toh(f->header->header_size) + le64toh(f->header->arena_size))) + return -ENODATA; + + if (!VALID64(le64toh(f->header->data_hash_table_offset)) || + !VALID64(le64toh(f->header->field_hash_table_offset)) || + !VALID64(le64toh(f->header->tail_object_offset)) || + !VALID64(le64toh(f->header->entry_array_offset))) + return -ENODATA; + + if (f->writable) { + uint8_t state; + sd_id128_t machine_id; + int r; + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + + if (!sd_id128_equal(machine_id, f->header->machine_id)) + return -EHOSTDOWN; + + state = f->header->state; + + if (state == STATE_ONLINE) { + log_debug("Journal file %s is already online. Assuming unclean closing.", f->path); + return -EBUSY; + } else if (state == STATE_ARCHIVED) + return -ESHUTDOWN; + else if (state != STATE_OFFLINE) { + log_debug("Journal file %s has unknown state %i.", f->path, state); + return -EBUSY; + } + } + + f->compress_xz = JOURNAL_HEADER_COMPRESSED_XZ(f->header); + f->compress_lz4 = JOURNAL_HEADER_COMPRESSED_LZ4(f->header); + + f->seal = JOURNAL_HEADER_SEALED(f->header); + + return 0; +} + +static int journal_file_fstat(JournalFile *f) { + assert(f); + assert(f->fd >= 0); + + if (fstat(f->fd, &f->last_stat) < 0) + return -errno; + + f->last_stat_usec = now(CLOCK_MONOTONIC); + + /* Refuse appending to files that are already deleted */ + if (f->last_stat.st_nlink <= 0) + return -EIDRM; + + return 0; +} + +static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) { + uint64_t old_size, new_size; + int r; + + assert(f); + assert(f->header); + + /* We assume that this file is not sparse, and we know that + * for sure, since we always call posix_fallocate() + * ourselves */ + + if (mmap_cache_got_sigbus(f->mmap, f->fd)) + return -EIO; + + old_size = + le64toh(f->header->header_size) + + le64toh(f->header->arena_size); + + new_size = PAGE_ALIGN(offset + size); + if (new_size < le64toh(f->header->header_size)) + new_size = le64toh(f->header->header_size); + + if (new_size <= old_size) { + + /* We already pre-allocated enough space, but before + * we write to it, let's check with fstat() if the + * file got deleted, in order make sure we don't throw + * away the data immediately. Don't check fstat() for + * all writes though, but only once ever 10s. */ + + if (f->last_stat_usec + LAST_STAT_REFRESH_USEC > now(CLOCK_MONOTONIC)) + return 0; + + return journal_file_fstat(f); + } + + /* Allocate more space. */ + + if (f->metrics.max_size > 0 && new_size > f->metrics.max_size) + return -E2BIG; + + if (new_size > f->metrics.min_size && f->metrics.keep_free > 0) { + struct statvfs svfs; + + if (fstatvfs(f->fd, &svfs) >= 0) { + uint64_t available; + + available = LESS_BY((uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize, f->metrics.keep_free); + + if (new_size - old_size > available) + return -E2BIG; + } + } + + /* Increase by larger blocks at once */ + new_size = ((new_size+FILE_SIZE_INCREASE-1) / FILE_SIZE_INCREASE) * FILE_SIZE_INCREASE; + if (f->metrics.max_size > 0 && new_size > f->metrics.max_size) + new_size = f->metrics.max_size; + + /* Note that the glibc fallocate() fallback is very + inefficient, hence we try to minimize the allocation area + as we can. */ + r = posix_fallocate(f->fd, old_size, new_size - old_size); + if (r != 0) + return -r; + + f->header->arena_size = htole64(new_size - le64toh(f->header->header_size)); + + return journal_file_fstat(f); +} + +static unsigned type_to_context(ObjectType type) { + /* One context for each type, plus one catch-all for the rest */ + assert_cc(_OBJECT_TYPE_MAX <= MMAP_CACHE_MAX_CONTEXTS); + assert_cc(CONTEXT_HEADER < MMAP_CACHE_MAX_CONTEXTS); + return type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX ? type : 0; +} + +static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_always, uint64_t offset, uint64_t size, void **ret) { + int r; + + assert(f); + assert(ret); + + if (size <= 0) + return -EINVAL; + + /* Avoid SIGBUS on invalid accesses */ + if (offset + size > (uint64_t) f->last_stat.st_size) { + /* Hmm, out of range? Let's refresh the fstat() data + * first, before we trust that check. */ + + r = journal_file_fstat(f); + if (r < 0) + return r; + + if (offset + size > (uint64_t) f->last_stat.st_size) + return -EADDRNOTAVAIL; + } + + return mmap_cache_get(f->mmap, f->fd, f->prot, type_to_context(type), keep_always, offset, size, &f->last_stat, ret); +} + +static uint64_t minimum_header_size(Object *o) { + + static const uint64_t table[] = { + [OBJECT_DATA] = sizeof(DataObject), + [OBJECT_FIELD] = sizeof(FieldObject), + [OBJECT_ENTRY] = sizeof(EntryObject), + [OBJECT_DATA_HASH_TABLE] = sizeof(HashTableObject), + [OBJECT_FIELD_HASH_TABLE] = sizeof(HashTableObject), + [OBJECT_ENTRY_ARRAY] = sizeof(EntryArrayObject), + [OBJECT_TAG] = sizeof(TagObject), + }; + + if (o->object.type >= ELEMENTSOF(table) || table[o->object.type] <= 0) + return sizeof(ObjectHeader); + + return table[o->object.type]; +} + +int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret) { + int r; + void *t; + Object *o; + uint64_t s; + + assert(f); + assert(ret); + + /* Objects may only be located at multiple of 64 bit */ + if (!VALID64(offset)) + return -EBADMSG; + + /* Object may not be located in the file header */ + if (offset < le64toh(f->header->header_size)) + return -EBADMSG; + + r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t); + if (r < 0) + return r; + + o = (Object*) t; + s = le64toh(o->object.size); + + if (s < sizeof(ObjectHeader)) + return -EBADMSG; + + if (o->object.type <= OBJECT_UNUSED) + return -EBADMSG; + + if (s < minimum_header_size(o)) + return -EBADMSG; + + if (type > OBJECT_UNUSED && o->object.type != type) + return -EBADMSG; + + if (s > sizeof(ObjectHeader)) { + r = journal_file_move_to(f, type, false, offset, s, &t); + if (r < 0) + return r; + + o = (Object*) t; + } + + *ret = o; + return 0; +} + +static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) { + uint64_t r; + + assert(f); + assert(f->header); + + r = le64toh(f->header->tail_entry_seqnum) + 1; + + if (seqnum) { + /* If an external seqnum counter was passed, we update + * both the local and the external one, and set it to + * the maximum of both */ + + if (*seqnum + 1 > r) + r = *seqnum + 1; + + *seqnum = r; + } + + f->header->tail_entry_seqnum = htole64(r); + + if (f->header->head_entry_seqnum == 0) + f->header->head_entry_seqnum = htole64(r); + + return r; +} + +int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset) { + int r; + uint64_t p; + Object *tail, *o; + void *t; + + assert(f); + assert(f->header); + assert(type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX); + assert(size >= sizeof(ObjectHeader)); + assert(offset); + assert(ret); + + r = journal_file_set_online(f); + if (r < 0) + return r; + + p = le64toh(f->header->tail_object_offset); + if (p == 0) + p = le64toh(f->header->header_size); + else { + r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail); + if (r < 0) + return r; + + p += ALIGN64(le64toh(tail->object.size)); + } + + r = journal_file_allocate(f, p, size); + if (r < 0) + return r; + + r = journal_file_move_to(f, type, false, p, size, &t); + if (r < 0) + return r; + + o = (Object*) t; + + zero(o->object); + o->object.type = type; + o->object.size = htole64(size); + + f->header->tail_object_offset = htole64(p); + f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1); + + *ret = o; + *offset = p; + + return 0; +} + +static int journal_file_setup_data_hash_table(JournalFile *f) { + uint64_t s, p; + Object *o; + int r; + + assert(f); + assert(f->header); + + /* We estimate that we need 1 hash table entry per 768 bytes + of journal file and we want to make sure we never get + beyond 75% fill level. Calculate the hash table size for + the maximum file size based on these metrics. */ + + s = (f->metrics.max_size * 4 / 768 / 3) * sizeof(HashItem); + if (s < DEFAULT_DATA_HASH_TABLE_SIZE) + s = DEFAULT_DATA_HASH_TABLE_SIZE; + + log_debug("Reserving %"PRIu64" entries in hash table.", s / sizeof(HashItem)); + + r = journal_file_append_object(f, + OBJECT_DATA_HASH_TABLE, + offsetof(Object, hash_table.items) + s, + &o, &p); + if (r < 0) + return r; + + memzero(o->hash_table.items, s); + + f->header->data_hash_table_offset = htole64(p + offsetof(Object, hash_table.items)); + f->header->data_hash_table_size = htole64(s); + + return 0; +} + +static int journal_file_setup_field_hash_table(JournalFile *f) { + uint64_t s, p; + Object *o; + int r; + + assert(f); + assert(f->header); + + /* We use a fixed size hash table for the fields as this + * number should grow very slowly only */ + + s = DEFAULT_FIELD_HASH_TABLE_SIZE; + r = journal_file_append_object(f, + OBJECT_FIELD_HASH_TABLE, + offsetof(Object, hash_table.items) + s, + &o, &p); + if (r < 0) + return r; + + memzero(o->hash_table.items, s); + + f->header->field_hash_table_offset = htole64(p + offsetof(Object, hash_table.items)); + f->header->field_hash_table_size = htole64(s); + + return 0; +} + +int journal_file_map_data_hash_table(JournalFile *f) { + uint64_t s, p; + void *t; + int r; + + assert(f); + assert(f->header); + + if (f->data_hash_table) + return 0; + + p = le64toh(f->header->data_hash_table_offset); + s = le64toh(f->header->data_hash_table_size); + + r = journal_file_move_to(f, + OBJECT_DATA_HASH_TABLE, + true, + p, s, + &t); + if (r < 0) + return r; + + f->data_hash_table = t; + return 0; +} + +int journal_file_map_field_hash_table(JournalFile *f) { + uint64_t s, p; + void *t; + int r; + + assert(f); + assert(f->header); + + if (f->field_hash_table) + return 0; + + p = le64toh(f->header->field_hash_table_offset); + s = le64toh(f->header->field_hash_table_size); + + r = journal_file_move_to(f, + OBJECT_FIELD_HASH_TABLE, + true, + p, s, + &t); + if (r < 0) + return r; + + f->field_hash_table = t; + return 0; +} + +static int journal_file_link_field( + JournalFile *f, + Object *o, + uint64_t offset, + uint64_t hash) { + + uint64_t p, h, m; + int r; + + assert(f); + assert(f->header); + assert(f->field_hash_table); + assert(o); + assert(offset > 0); + + if (o->object.type != OBJECT_FIELD) + return -EINVAL; + + m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem); + if (m <= 0) + return -EBADMSG; + + /* This might alter the window we are looking at */ + o->field.next_hash_offset = o->field.head_data_offset = 0; + + h = hash % m; + p = le64toh(f->field_hash_table[h].tail_hash_offset); + if (p == 0) + f->field_hash_table[h].head_hash_offset = htole64(offset); + else { + r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o); + if (r < 0) + return r; + + o->field.next_hash_offset = htole64(offset); + } + + f->field_hash_table[h].tail_hash_offset = htole64(offset); + + if (JOURNAL_HEADER_CONTAINS(f->header, n_fields)) + f->header->n_fields = htole64(le64toh(f->header->n_fields) + 1); + + return 0; +} + +static int journal_file_link_data( + JournalFile *f, + Object *o, + uint64_t offset, + uint64_t hash) { + + uint64_t p, h, m; + int r; + + assert(f); + assert(f->header); + assert(f->data_hash_table); + assert(o); + assert(offset > 0); + + if (o->object.type != OBJECT_DATA) + return -EINVAL; + + m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); + if (m <= 0) + return -EBADMSG; + + /* This might alter the window we are looking at */ + o->data.next_hash_offset = o->data.next_field_offset = 0; + o->data.entry_offset = o->data.entry_array_offset = 0; + o->data.n_entries = 0; + + h = hash % m; + p = le64toh(f->data_hash_table[h].tail_hash_offset); + if (p == 0) + /* Only entry in the hash table is easy */ + f->data_hash_table[h].head_hash_offset = htole64(offset); + else { + /* Move back to the previous data object, to patch in + * pointer */ + + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + o->data.next_hash_offset = htole64(offset); + } + + f->data_hash_table[h].tail_hash_offset = htole64(offset); + + if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) + f->header->n_data = htole64(le64toh(f->header->n_data) + 1); + + return 0; +} + +int journal_file_find_field_object_with_hash( + JournalFile *f, + const void *field, uint64_t size, uint64_t hash, + Object **ret, uint64_t *offset) { + + uint64_t p, osize, h, m; + int r; + + assert(f); + assert(f->header); + assert(field && size > 0); + + /* If the field hash table is empty, we can't find anything */ + if (le64toh(f->header->field_hash_table_size) <= 0) + return 0; + + /* Map the field hash table, if it isn't mapped yet. */ + r = journal_file_map_field_hash_table(f); + if (r < 0) + return r; + + osize = offsetof(Object, field.payload) + size; + + m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem); + if (m <= 0) + return -EBADMSG; + + h = hash % m; + p = le64toh(f->field_hash_table[h].head_hash_offset); + + while (p > 0) { + Object *o; + + r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o); + if (r < 0) + return r; + + if (le64toh(o->field.hash) == hash && + le64toh(o->object.size) == osize && + memcmp(o->field.payload, field, size) == 0) { + + if (ret) + *ret = o; + if (offset) + *offset = p; + + return 1; + } + + p = le64toh(o->field.next_hash_offset); + } + + return 0; +} + +int journal_file_find_field_object( + JournalFile *f, + const void *field, uint64_t size, + Object **ret, uint64_t *offset) { + + uint64_t hash; + + assert(f); + assert(field && size > 0); + + hash = hash64(field, size); + + return journal_file_find_field_object_with_hash(f, + field, size, hash, + ret, offset); +} + +int journal_file_find_data_object_with_hash( + JournalFile *f, + const void *data, uint64_t size, uint64_t hash, + Object **ret, uint64_t *offset) { + + uint64_t p, osize, h, m; + int r; + + assert(f); + assert(f->header); + assert(data || size == 0); + + /* If there's no data hash table, then there's no entry. */ + if (le64toh(f->header->data_hash_table_size) <= 0) + return 0; + + /* Map the data hash table, if it isn't mapped yet. */ + r = journal_file_map_data_hash_table(f); + if (r < 0) + return r; + + osize = offsetof(Object, data.payload) + size; + + m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); + if (m <= 0) + return -EBADMSG; + + h = hash % m; + p = le64toh(f->data_hash_table[h].head_hash_offset); + + while (p > 0) { + Object *o; + + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + if (le64toh(o->data.hash) != hash) + goto next; + + if (o->object.flags & OBJECT_COMPRESSION_MASK) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + uint64_t l; + size_t rsize = 0; + + l = le64toh(o->object.size); + if (l <= offsetof(Object, data.payload)) + return -EBADMSG; + + l -= offsetof(Object, data.payload); + + r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK, + o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, 0); + if (r < 0) + return r; + + if (rsize == size && + memcmp(f->compress_buffer, data, size) == 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } +#else + return -EPROTONOSUPPORT; +#endif + } else if (le64toh(o->object.size) == osize && + memcmp(o->data.payload, data, size) == 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } + + next: + p = le64toh(o->data.next_hash_offset); + } + + return 0; +} + +int journal_file_find_data_object( + JournalFile *f, + const void *data, uint64_t size, + Object **ret, uint64_t *offset) { + + uint64_t hash; + + assert(f); + assert(data || size == 0); + + hash = hash64(data, size); + + return journal_file_find_data_object_with_hash(f, + data, size, hash, + ret, offset); +} + +static int journal_file_append_field( + JournalFile *f, + const void *field, uint64_t size, + Object **ret, uint64_t *offset) { + + uint64_t hash, p; + uint64_t osize; + Object *o; + int r; + + assert(f); + assert(field && size > 0); + + hash = hash64(field, size); + + r = journal_file_find_field_object_with_hash(f, field, size, hash, &o, &p); + if (r < 0) + return r; + else if (r > 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; + } + + osize = offsetof(Object, field.payload) + size; + r = journal_file_append_object(f, OBJECT_FIELD, osize, &o, &p); + if (r < 0) + return r; + + o->field.hash = htole64(hash); + memcpy(o->field.payload, field, size); + + r = journal_file_link_field(f, o, p, hash); + if (r < 0) + return r; + + /* The linking might have altered the window, so let's + * refresh our pointer */ + r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o); + if (r < 0) + return r; + +#ifdef HAVE_GCRYPT + r = journal_file_hmac_put_object(f, OBJECT_FIELD, o, p); + if (r < 0) + return r; +#endif + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; +} + +static int journal_file_append_data( + JournalFile *f, + const void *data, uint64_t size, + Object **ret, uint64_t *offset) { + + uint64_t hash, p; + uint64_t osize; + Object *o; + int r, compression = 0; + const void *eq; + + assert(f); + assert(data || size == 0); + + hash = hash64(data, size); + + r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p); + if (r < 0) + return r; + if (r > 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; + } + + osize = offsetof(Object, data.payload) + size; + r = journal_file_append_object(f, OBJECT_DATA, osize, &o, &p); + if (r < 0) + return r; + + o->data.hash = htole64(hash); + +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + if (JOURNAL_FILE_COMPRESS(f) && size >= COMPRESSION_SIZE_THRESHOLD) { + size_t rsize = 0; + + compression = compress_blob(data, size, o->data.payload, size - 1, &rsize); + + if (compression >= 0) { + o->object.size = htole64(offsetof(Object, data.payload) + rsize); + o->object.flags |= compression; + + log_debug("Compressed data object %"PRIu64" -> %zu using %s", + size, rsize, object_compressed_to_string(compression)); + } else + /* Compression didn't work, we don't really care why, let's continue without compression */ + compression = 0; + } +#endif + + if (compression == 0) + memcpy_safe(o->data.payload, data, size); + + r = journal_file_link_data(f, o, p, hash); + if (r < 0) + return r; + + /* The linking might have altered the window, so let's + * refresh our pointer */ + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + if (!data) + eq = NULL; + else + eq = memchr(data, '=', size); + if (eq && eq > data) { + Object *fo = NULL; + uint64_t fp; + + /* Create field object ... */ + r = journal_file_append_field(f, data, (uint8_t*) eq - (uint8_t*) data, &fo, &fp); + if (r < 0) + return r; + + /* ... and link it in. */ + o->data.next_field_offset = fo->field.head_data_offset; + fo->field.head_data_offset = le64toh(p); + } + +#ifdef HAVE_GCRYPT + r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p); + if (r < 0) + return r; +#endif + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; +} + +uint64_t journal_file_entry_n_items(Object *o) { + assert(o); + + if (o->object.type != OBJECT_ENTRY) + return 0; + + return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem); +} + +uint64_t journal_file_entry_array_n_items(Object *o) { + assert(o); + + if (o->object.type != OBJECT_ENTRY_ARRAY) + return 0; + + return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t); +} + +uint64_t journal_file_hash_table_n_items(Object *o) { + assert(o); + + if (o->object.type != OBJECT_DATA_HASH_TABLE && + o->object.type != OBJECT_FIELD_HASH_TABLE) + return 0; + + return (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem); +} + +static int link_entry_into_array(JournalFile *f, + le64_t *first, + le64_t *idx, + uint64_t p) { + int r; + uint64_t n = 0, ap = 0, q, i, a, hidx; + Object *o; + + assert(f); + assert(f->header); + assert(first); + assert(idx); + assert(p > 0); + + a = le64toh(*first); + i = hidx = le64toh(*idx); + while (a > 0) { + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + n = journal_file_entry_array_n_items(o); + if (i < n) { + o->entry_array.items[i] = htole64(p); + *idx = htole64(hidx + 1); + return 0; + } + + i -= n; + ap = a; + a = le64toh(o->entry_array.next_entry_array_offset); + } + + if (hidx > n) + n = (hidx+1) * 2; + else + n = n * 2; + + if (n < 4) + n = 4; + + r = journal_file_append_object(f, OBJECT_ENTRY_ARRAY, + offsetof(Object, entry_array.items) + n * sizeof(uint64_t), + &o, &q); + if (r < 0) + return r; + +#ifdef HAVE_GCRYPT + r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, o, q); + if (r < 0) + return r; +#endif + + o->entry_array.items[i] = htole64(p); + + if (ap == 0) + *first = htole64(q); + else { + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, ap, &o); + if (r < 0) + return r; + + o->entry_array.next_entry_array_offset = htole64(q); + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) + f->header->n_entry_arrays = htole64(le64toh(f->header->n_entry_arrays) + 1); + + *idx = htole64(hidx + 1); + + return 0; +} + +static int link_entry_into_array_plus_one(JournalFile *f, + le64_t *extra, + le64_t *first, + le64_t *idx, + uint64_t p) { + + int r; + + assert(f); + assert(extra); + assert(first); + assert(idx); + assert(p > 0); + + if (*idx == 0) + *extra = htole64(p); + else { + le64_t i; + + i = htole64(le64toh(*idx) - 1); + r = link_entry_into_array(f, first, &i, p); + if (r < 0) + return r; + } + + *idx = htole64(le64toh(*idx) + 1); + return 0; +} + +static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) { + uint64_t p; + int r; + assert(f); + assert(o); + assert(offset > 0); + + p = le64toh(o->entry.items[i].object_offset); + if (p == 0) + return -EINVAL; + + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + return link_entry_into_array_plus_one(f, + &o->data.entry_offset, + &o->data.entry_array_offset, + &o->data.n_entries, + offset); +} + +static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) { + uint64_t n, i; + int r; + + assert(f); + assert(f->header); + assert(o); + assert(offset > 0); + + if (o->object.type != OBJECT_ENTRY) + return -EINVAL; + + __sync_synchronize(); + + /* Link up the entry itself */ + r = link_entry_into_array(f, + &f->header->entry_array_offset, + &f->header->n_entries, + offset); + if (r < 0) + return r; + + /* log_debug("=> %s seqnr=%"PRIu64" n_entries=%"PRIu64, f->path, o->entry.seqnum, f->header->n_entries); */ + + if (f->header->head_entry_realtime == 0) + f->header->head_entry_realtime = o->entry.realtime; + + f->header->tail_entry_realtime = o->entry.realtime; + f->header->tail_entry_monotonic = o->entry.monotonic; + + f->tail_entry_monotonic_valid = true; + + /* Link up the items */ + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) { + r = journal_file_link_entry_item(f, o, offset, i); + if (r < 0) + return r; + } + + return 0; +} + +static int journal_file_append_entry_internal( + JournalFile *f, + const dual_timestamp *ts, + uint64_t xor_hash, + const EntryItem items[], unsigned n_items, + uint64_t *seqnum, + Object **ret, uint64_t *offset) { + uint64_t np; + uint64_t osize; + Object *o; + int r; + + assert(f); + assert(f->header); + assert(items || n_items == 0); + assert(ts); + + osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem)); + + r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np); + if (r < 0) + return r; + + o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum)); + memcpy_safe(o->entry.items, items, n_items * sizeof(EntryItem)); + o->entry.realtime = htole64(ts->realtime); + o->entry.monotonic = htole64(ts->monotonic); + o->entry.xor_hash = htole64(xor_hash); + o->entry.boot_id = f->header->boot_id; + +#ifdef HAVE_GCRYPT + r = journal_file_hmac_put_object(f, OBJECT_ENTRY, o, np); + if (r < 0) + return r; +#endif + + r = journal_file_link_entry(f, o, np); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = np; + + return 0; +} + +void journal_file_post_change(JournalFile *f) { + assert(f); + + /* inotify() does not receive IN_MODIFY events from file + * accesses done via mmap(). After each access we hence + * trigger IN_MODIFY by truncating the journal file to its + * current size which triggers IN_MODIFY. */ + + __sync_synchronize(); + + if (ftruncate(f->fd, f->last_stat.st_size) < 0) + log_debug_errno(errno, "Failed to truncate file to its own size: %m"); +} + +static int post_change_thunk(sd_event_source *timer, uint64_t usec, void *userdata) { + assert(userdata); + + journal_file_post_change(userdata); + + return 1; +} + +static void schedule_post_change(JournalFile *f) { + sd_event_source *timer; + int enabled, r; + uint64_t now; + + assert(f); + assert(f->post_change_timer); + + timer = f->post_change_timer; + + r = sd_event_source_get_enabled(timer, &enabled); + if (r < 0) { + log_debug_errno(r, "Failed to get ftruncate timer state: %m"); + goto fail; + } + + if (enabled == SD_EVENT_ONESHOT) + return; + + r = sd_event_now(sd_event_source_get_event(timer), CLOCK_MONOTONIC, &now); + if (r < 0) { + log_debug_errno(r, "Failed to get clock's now for scheduling ftruncate: %m"); + goto fail; + } + + r = sd_event_source_set_time(timer, now+f->post_change_timer_period); + if (r < 0) { + log_debug_errno(r, "Failed to set time for scheduling ftruncate: %m"); + goto fail; + } + + r = sd_event_source_set_enabled(timer, SD_EVENT_ONESHOT); + if (r < 0) { + log_debug_errno(r, "Failed to enable scheduled ftruncate: %m"); + goto fail; + } + + return; + +fail: + /* On failure, let's simply post the change immediately. */ + journal_file_post_change(f); +} + +/* Enable coalesced change posting in a timer on the provided sd_event instance */ +int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t) { + _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL; + int r; + + assert(f); + assert_return(!f->post_change_timer, -EINVAL); + assert(e); + assert(t); + + r = sd_event_add_time(e, &timer, CLOCK_MONOTONIC, 0, 0, post_change_thunk, f); + if (r < 0) + return r; + + r = sd_event_source_set_enabled(timer, SD_EVENT_OFF); + if (r < 0) + return r; + + f->post_change_timer = timer; + timer = NULL; + f->post_change_timer_period = t; + + return r; +} + +static int entry_item_cmp(const void *_a, const void *_b) { + const EntryItem *a = _a, *b = _b; + + if (le64toh(a->object_offset) < le64toh(b->object_offset)) + return -1; + if (le64toh(a->object_offset) > le64toh(b->object_offset)) + return 1; + return 0; +} + +int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqnum, Object **ret, uint64_t *offset) { + unsigned i; + EntryItem *items; + int r; + uint64_t xor_hash = 0; + struct dual_timestamp _ts; + + assert(f); + assert(f->header); + assert(iovec || n_iovec == 0); + + if (!ts) { + dual_timestamp_get(&_ts); + ts = &_ts; + } + +#ifdef HAVE_GCRYPT + r = journal_file_maybe_append_tag(f, ts->realtime); + if (r < 0) + return r; +#endif + + /* alloca() can't take 0, hence let's allocate at least one */ + items = alloca(sizeof(EntryItem) * MAX(1u, n_iovec)); + + for (i = 0; i < n_iovec; i++) { + uint64_t p; + Object *o; + + r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p); + if (r < 0) + return r; + + xor_hash ^= le64toh(o->data.hash); + items[i].object_offset = htole64(p); + items[i].hash = o->data.hash; + } + + /* Order by the position on disk, in order to improve seek + * times for rotating media. */ + qsort_safe(items, n_iovec, sizeof(EntryItem), entry_item_cmp); + + r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqnum, ret, offset); + + /* If the memory mapping triggered a SIGBUS then we return an + * IO error and ignore the error code passed down to us, since + * it is very likely just an effect of a nullified replacement + * mapping page */ + + if (mmap_cache_got_sigbus(f->mmap, f->fd)) + r = -EIO; + + if (f->post_change_timer) + schedule_post_change(f); + else + journal_file_post_change(f); + + return r; +} + +typedef struct ChainCacheItem { + uint64_t first; /* the array at the beginning of the chain */ + uint64_t array; /* the cached array */ + uint64_t begin; /* the first item in the cached array */ + uint64_t total; /* the total number of items in all arrays before this one in the chain */ + uint64_t last_index; /* the last index we looked at, to optimize locality when bisecting */ +} ChainCacheItem; + +static void chain_cache_put( + OrderedHashmap *h, + ChainCacheItem *ci, + uint64_t first, + uint64_t array, + uint64_t begin, + uint64_t total, + uint64_t last_index) { + + if (!ci) { + /* If the chain item to cache for this chain is the + * first one it's not worth caching anything */ + if (array == first) + return; + + if (ordered_hashmap_size(h) >= CHAIN_CACHE_MAX) { + ci = ordered_hashmap_steal_first(h); + assert(ci); + } else { + ci = new(ChainCacheItem, 1); + if (!ci) + return; + } + + ci->first = first; + + if (ordered_hashmap_put(h, &ci->first, ci) < 0) { + free(ci); + return; + } + } else + assert(ci->first == first); + + ci->array = array; + ci->begin = begin; + ci->total = total; + ci->last_index = last_index; +} + +static int generic_array_get( + JournalFile *f, + uint64_t first, + uint64_t i, + Object **ret, uint64_t *offset) { + + Object *o; + uint64_t p = 0, a, t = 0; + int r; + ChainCacheItem *ci; + + assert(f); + + a = first; + + /* Try the chain cache first */ + ci = ordered_hashmap_get(f->chain_cache, &first); + if (ci && i > ci->total) { + a = ci->array; + i -= ci->total; + t = ci->total; + } + + while (a > 0) { + uint64_t k; + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + k = journal_file_entry_array_n_items(o); + if (i < k) { + p = le64toh(o->entry_array.items[i]); + goto found; + } + + i -= k; + t += k; + a = le64toh(o->entry_array.next_entry_array_offset); + } + + return 0; + +found: + /* Let's cache this item for the next invocation */ + chain_cache_put(f->chain_cache, ci, first, a, le64toh(o->entry_array.items[0]), t, i); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; +} + +static int generic_array_get_plus_one( + JournalFile *f, + uint64_t extra, + uint64_t first, + uint64_t i, + Object **ret, uint64_t *offset) { + + Object *o; + + assert(f); + + if (i == 0) { + int r; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = extra; + + return 1; + } + + return generic_array_get(f, first, i-1, ret, offset); +} + +enum { + TEST_FOUND, + TEST_LEFT, + TEST_RIGHT +}; + +static int generic_array_bisect( + JournalFile *f, + uint64_t first, + uint64_t n, + uint64_t needle, + int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle), + direction_t direction, + Object **ret, + uint64_t *offset, + uint64_t *idx) { + + uint64_t a, p, t = 0, i = 0, last_p = 0, last_index = (uint64_t) -1; + bool subtract_one = false; + Object *o, *array = NULL; + int r; + ChainCacheItem *ci; + + assert(f); + assert(test_object); + + /* Start with the first array in the chain */ + a = first; + + ci = ordered_hashmap_get(f->chain_cache, &first); + if (ci && n > ci->total) { + /* Ah, we have iterated this bisection array chain + * previously! Let's see if we can skip ahead in the + * chain, as far as the last time. But we can't jump + * backwards in the chain, so let's check that + * first. */ + + r = test_object(f, ci->begin, needle); + if (r < 0) + return r; + + if (r == TEST_LEFT) { + /* OK, what we are looking for is right of the + * begin of this EntryArray, so let's jump + * straight to previously cached array in the + * chain */ + + a = ci->array; + n -= ci->total; + t = ci->total; + last_index = ci->last_index; + } + } + + while (a > 0) { + uint64_t left, right, k, lp; + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array); + if (r < 0) + return r; + + k = journal_file_entry_array_n_items(array); + right = MIN(k, n); + if (right <= 0) + return 0; + + i = right - 1; + lp = p = le64toh(array->entry_array.items[i]); + if (p <= 0) + r = -EBADMSG; + else + r = test_object(f, p, needle); + if (r == -EBADMSG) { + log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (1)"); + n = i; + continue; + } + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + if (r == TEST_RIGHT) { + left = 0; + right -= 1; + + if (last_index != (uint64_t) -1) { + assert(last_index <= right); + + /* If we cached the last index we + * looked at, let's try to not to jump + * too wildly around and see if we can + * limit the range to look at early to + * the immediate neighbors of the last + * index we looked at. */ + + if (last_index > 0) { + uint64_t x = last_index - 1; + + p = le64toh(array->entry_array.items[x]); + if (p <= 0) + return -EBADMSG; + + r = test_object(f, p, needle); + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + if (r == TEST_RIGHT) + right = x; + else + left = x + 1; + } + + if (last_index < right) { + uint64_t y = last_index + 1; + + p = le64toh(array->entry_array.items[y]); + if (p <= 0) + return -EBADMSG; + + r = test_object(f, p, needle); + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + if (r == TEST_RIGHT) + right = y; + else + left = y + 1; + } + } + + for (;;) { + if (left == right) { + if (direction == DIRECTION_UP) + subtract_one = true; + + i = left; + goto found; + } + + assert(left < right); + i = (left + right) / 2; + + p = le64toh(array->entry_array.items[i]); + if (p <= 0) + r = -EBADMSG; + else + r = test_object(f, p, needle); + if (r == -EBADMSG) { + log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (2)"); + right = n = i; + continue; + } + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + if (r == TEST_RIGHT) + right = i; + else + left = i + 1; + } + } + + if (k >= n) { + if (direction == DIRECTION_UP) { + i = n; + subtract_one = true; + goto found; + } + + return 0; + } + + last_p = lp; + + n -= k; + t += k; + last_index = (uint64_t) -1; + a = le64toh(array->entry_array.next_entry_array_offset); + } + + return 0; + +found: + if (subtract_one && t == 0 && i == 0) + return 0; + + /* Let's cache this item for the next invocation */ + chain_cache_put(f->chain_cache, ci, first, a, le64toh(array->entry_array.items[0]), t, subtract_one ? (i > 0 ? i-1 : (uint64_t) -1) : i); + + if (subtract_one && i == 0) + p = last_p; + else if (subtract_one) + p = le64toh(array->entry_array.items[i-1]); + else + p = le64toh(array->entry_array.items[i]); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + if (idx) + *idx = t + i + (subtract_one ? -1 : 0); + + return 1; +} + +static int generic_array_bisect_plus_one( + JournalFile *f, + uint64_t extra, + uint64_t first, + uint64_t n, + uint64_t needle, + int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle), + direction_t direction, + Object **ret, + uint64_t *offset, + uint64_t *idx) { + + int r; + bool step_back = false; + Object *o; + + assert(f); + assert(test_object); + + if (n <= 0) + return 0; + + /* This bisects the array in object 'first', but first checks + * an extra */ + r = test_object(f, extra, needle); + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + /* if we are looking with DIRECTION_UP then we need to first + see if in the actual array there is a matching entry, and + return the last one of that. But if there isn't any we need + to return this one. Hence remember this, and return it + below. */ + if (r == TEST_LEFT) + step_back = direction == DIRECTION_UP; + + if (r == TEST_RIGHT) { + if (direction == DIRECTION_DOWN) + goto found; + else + return 0; + } + + r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx); + + if (r == 0 && step_back) + goto found; + + if (r > 0 && idx) + (*idx)++; + + return r; + +found: + r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = extra; + + if (idx) + *idx = 0; + + return 1; +} + +_pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) { + assert(f); + assert(p > 0); + + if (p == needle) + return TEST_FOUND; + else if (p < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; + + assert(f); + assert(p > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (le64toh(o->entry.seqnum) == needle) + return TEST_FOUND; + else if (le64toh(o->entry.seqnum) < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_move_to_entry_by_seqnum( + JournalFile *f, + uint64_t seqnum, + direction_t direction, + Object **ret, + uint64_t *offset) { + assert(f); + assert(f->header); + + return generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + seqnum, + test_object_seqnum, + direction, + ret, offset, NULL); +} + +static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; + + assert(f); + assert(p > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (le64toh(o->entry.realtime) == needle) + return TEST_FOUND; + else if (le64toh(o->entry.realtime) < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_move_to_entry_by_realtime( + JournalFile *f, + uint64_t realtime, + direction_t direction, + Object **ret, + uint64_t *offset) { + assert(f); + assert(f->header); + + return generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + realtime, + test_object_realtime, + direction, + ret, offset, NULL); +} + +static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; + + assert(f); + assert(p > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (le64toh(o->entry.monotonic) == needle) + return TEST_FOUND; + else if (le64toh(o->entry.monotonic) < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +static int find_data_object_by_boot_id( + JournalFile *f, + sd_id128_t boot_id, + Object **o, + uint64_t *b) { + + char t[sizeof("_BOOT_ID=")-1 + 32 + 1] = "_BOOT_ID="; + + sd_id128_to_string(boot_id, t + 9); + return journal_file_find_data_object(f, t, sizeof(t) - 1, o, b); +} + +int journal_file_move_to_entry_by_monotonic( + JournalFile *f, + sd_id128_t boot_id, + uint64_t monotonic, + direction_t direction, + Object **ret, + uint64_t *offset) { + + Object *o; + int r; + + assert(f); + + r = find_data_object_by_boot_id(f, boot_id, &o, NULL); + if (r < 0) + return r; + if (r == 0) + return -ENOENT; + + return generic_array_bisect_plus_one(f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries), + monotonic, + test_object_monotonic, + direction, + ret, offset, NULL); +} + +void journal_file_reset_location(JournalFile *f) { + f->location_type = LOCATION_HEAD; + f->current_offset = 0; + f->current_seqnum = 0; + f->current_realtime = 0; + f->current_monotonic = 0; + zero(f->current_boot_id); + f->current_xor_hash = 0; +} + +void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset) { + f->location_type = LOCATION_SEEK; + f->current_offset = offset; + f->current_seqnum = le64toh(o->entry.seqnum); + f->current_realtime = le64toh(o->entry.realtime); + f->current_monotonic = le64toh(o->entry.monotonic); + f->current_boot_id = o->entry.boot_id; + f->current_xor_hash = le64toh(o->entry.xor_hash); +} + +int journal_file_compare_locations(JournalFile *af, JournalFile *bf) { + assert(af); + assert(af->header); + assert(bf); + assert(bf->header); + assert(af->location_type == LOCATION_SEEK); + assert(bf->location_type == LOCATION_SEEK); + + /* If contents and timestamps match, these entries are + * identical, even if the seqnum does not match */ + if (sd_id128_equal(af->current_boot_id, bf->current_boot_id) && + af->current_monotonic == bf->current_monotonic && + af->current_realtime == bf->current_realtime && + af->current_xor_hash == bf->current_xor_hash) + return 0; + + if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) { + + /* If this is from the same seqnum source, compare + * seqnums */ + if (af->current_seqnum < bf->current_seqnum) + return -1; + if (af->current_seqnum > bf->current_seqnum) + return 1; + + /* Wow! This is weird, different data but the same + * seqnums? Something is borked, but let's make the + * best of it and compare by time. */ + } + + if (sd_id128_equal(af->current_boot_id, bf->current_boot_id)) { + + /* If the boot id matches, compare monotonic time */ + if (af->current_monotonic < bf->current_monotonic) + return -1; + if (af->current_monotonic > bf->current_monotonic) + return 1; + } + + /* Otherwise, compare UTC time */ + if (af->current_realtime < bf->current_realtime) + return -1; + if (af->current_realtime > bf->current_realtime) + return 1; + + /* Finally, compare by contents */ + if (af->current_xor_hash < bf->current_xor_hash) + return -1; + if (af->current_xor_hash > bf->current_xor_hash) + return 1; + + return 0; +} + +int journal_file_next_entry( + JournalFile *f, + uint64_t p, + direction_t direction, + Object **ret, uint64_t *offset) { + + uint64_t i, n, ofs; + int r; + + assert(f); + assert(f->header); + + n = le64toh(f->header->n_entries); + if (n <= 0) + return 0; + + if (p == 0) + i = direction == DIRECTION_DOWN ? 0 : n - 1; + else { + r = generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + p, + test_object_offset, + DIRECTION_DOWN, + NULL, NULL, + &i); + if (r <= 0) + return r; + + if (direction == DIRECTION_DOWN) { + if (i >= n - 1) + return 0; + + i++; + } else { + if (i <= 0) + return 0; + + i--; + } + } + + /* And jump to it */ + r = generic_array_get(f, + le64toh(f->header->entry_array_offset), + i, + ret, &ofs); + if (r == -EBADMSG && direction == DIRECTION_DOWN) { + /* Special case: when we iterate throught the journal file linearly, and hit an entry we can't read, + * consider this the end of the journal file. */ + log_debug_errno(r, "Encountered entry we can't read while iterating through journal file. Considering this the end of the file."); + return 0; + } + if (r <= 0) + return r; + + if (p > 0 && + (direction == DIRECTION_DOWN ? ofs <= p : ofs >= p)) { + log_debug("%s: entry array corrupted at entry %" PRIu64, f->path, i); + return -EBADMSG; + } + + if (offset) + *offset = ofs; + + return 1; +} + +int journal_file_next_entry_for_data( + JournalFile *f, + Object *o, uint64_t p, + uint64_t data_offset, + direction_t direction, + Object **ret, uint64_t *offset) { + + uint64_t n, i; + int r; + Object *d; + + assert(f); + assert(p > 0 || !o); + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + n = le64toh(d->data.n_entries); + if (n <= 0) + return n; + + if (!o) + i = direction == DIRECTION_DOWN ? 0 : n - 1; + else { + if (o->object.type != OBJECT_ENTRY) + return -EINVAL; + + r = generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + p, + test_object_offset, + DIRECTION_DOWN, + NULL, NULL, + &i); + + if (r <= 0) + return r; + + if (direction == DIRECTION_DOWN) { + if (i >= n - 1) + return 0; + + i++; + } else { + if (i <= 0) + return 0; + + i--; + } + + } + + return generic_array_get_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + i, + ret, offset); +} + +int journal_file_move_to_entry_by_offset_for_data( + JournalFile *f, + uint64_t data_offset, + uint64_t p, + direction_t direction, + Object **ret, uint64_t *offset) { + + int r; + Object *d; + + assert(f); + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + return generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + p, + test_object_offset, + direction, + ret, offset, NULL); +} + +int journal_file_move_to_entry_by_monotonic_for_data( + JournalFile *f, + uint64_t data_offset, + sd_id128_t boot_id, + uint64_t monotonic, + direction_t direction, + Object **ret, uint64_t *offset) { + + Object *o, *d; + int r; + uint64_t b, z; + + assert(f); + + /* First, seek by time */ + r = find_data_object_by_boot_id(f, boot_id, &o, &b); + if (r < 0) + return r; + if (r == 0) + return -ENOENT; + + r = generic_array_bisect_plus_one(f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries), + monotonic, + test_object_monotonic, + direction, + NULL, &z, NULL); + if (r <= 0) + return r; + + /* And now, continue seeking until we find an entry that + * exists in both bisection arrays */ + + for (;;) { + Object *qo; + uint64_t p, q; + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + r = generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + z, + test_object_offset, + direction, + NULL, &p, NULL); + if (r <= 0) + return r; + + r = journal_file_move_to_object(f, OBJECT_DATA, b, &o); + if (r < 0) + return r; + + r = generic_array_bisect_plus_one(f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries), + p, + test_object_offset, + direction, + &qo, &q, NULL); + + if (r <= 0) + return r; + + if (p == q) { + if (ret) + *ret = qo; + if (offset) + *offset = q; + + return 1; + } + + z = q; + } +} + +int journal_file_move_to_entry_by_seqnum_for_data( + JournalFile *f, + uint64_t data_offset, + uint64_t seqnum, + direction_t direction, + Object **ret, uint64_t *offset) { + + Object *d; + int r; + + assert(f); + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + return generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + seqnum, + test_object_seqnum, + direction, + ret, offset, NULL); +} + +int journal_file_move_to_entry_by_realtime_for_data( + JournalFile *f, + uint64_t data_offset, + uint64_t realtime, + direction_t direction, + Object **ret, uint64_t *offset) { + + Object *d; + int r; + + assert(f); + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + return generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + realtime, + test_object_realtime, + direction, + ret, offset, NULL); +} + +void journal_file_dump(JournalFile *f) { + Object *o; + int r; + uint64_t p; + + assert(f); + assert(f->header); + + journal_file_print_header(f); + + p = le64toh(f->header->header_size); + while (p != 0) { + r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); + if (r < 0) + goto fail; + + switch (o->object.type) { + + case OBJECT_UNUSED: + printf("Type: OBJECT_UNUSED\n"); + break; + + case OBJECT_DATA: + printf("Type: OBJECT_DATA\n"); + break; + + case OBJECT_FIELD: + printf("Type: OBJECT_FIELD\n"); + break; + + case OBJECT_ENTRY: + printf("Type: OBJECT_ENTRY seqnum=%"PRIu64" monotonic=%"PRIu64" realtime=%"PRIu64"\n", + le64toh(o->entry.seqnum), + le64toh(o->entry.monotonic), + le64toh(o->entry.realtime)); + break; + + case OBJECT_FIELD_HASH_TABLE: + printf("Type: OBJECT_FIELD_HASH_TABLE\n"); + break; + + case OBJECT_DATA_HASH_TABLE: + printf("Type: OBJECT_DATA_HASH_TABLE\n"); + break; + + case OBJECT_ENTRY_ARRAY: + printf("Type: OBJECT_ENTRY_ARRAY\n"); + break; + + case OBJECT_TAG: + printf("Type: OBJECT_TAG seqnum=%"PRIu64" epoch=%"PRIu64"\n", + le64toh(o->tag.seqnum), + le64toh(o->tag.epoch)); + break; + + default: + printf("Type: unknown (%i)\n", o->object.type); + break; + } + + if (o->object.flags & OBJECT_COMPRESSION_MASK) + printf("Flags: %s\n", + object_compressed_to_string(o->object.flags & OBJECT_COMPRESSION_MASK)); + + if (p == le64toh(f->header->tail_object_offset)) + p = 0; + else + p = p + ALIGN64(le64toh(o->object.size)); + } + + return; +fail: + log_error("File corrupt"); +} + +static const char* format_timestamp_safe(char *buf, size_t l, usec_t t) { + const char *x; + + x = format_timestamp(buf, l, t); + if (x) + return x; + return " --- "; +} + +void journal_file_print_header(JournalFile *f) { + char a[33], b[33], c[33], d[33]; + char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX], z[FORMAT_TIMESTAMP_MAX]; + struct stat st; + char bytes[FORMAT_BYTES_MAX]; + + assert(f); + assert(f->header); + + printf("File Path: %s\n" + "File ID: %s\n" + "Machine ID: %s\n" + "Boot ID: %s\n" + "Sequential Number ID: %s\n" + "State: %s\n" + "Compatible Flags:%s%s\n" + "Incompatible Flags:%s%s%s\n" + "Header size: %"PRIu64"\n" + "Arena size: %"PRIu64"\n" + "Data Hash Table Size: %"PRIu64"\n" + "Field Hash Table Size: %"PRIu64"\n" + "Rotate Suggested: %s\n" + "Head Sequential Number: %"PRIu64" (%"PRIx64")\n" + "Tail Sequential Number: %"PRIu64" (%"PRIx64")\n" + "Head Realtime Timestamp: %s (%"PRIx64")\n" + "Tail Realtime Timestamp: %s (%"PRIx64")\n" + "Tail Monotonic Timestamp: %s (%"PRIx64")\n" + "Objects: %"PRIu64"\n" + "Entry Objects: %"PRIu64"\n", + f->path, + sd_id128_to_string(f->header->file_id, a), + sd_id128_to_string(f->header->machine_id, b), + sd_id128_to_string(f->header->boot_id, c), + sd_id128_to_string(f->header->seqnum_id, d), + f->header->state == STATE_OFFLINE ? "OFFLINE" : + f->header->state == STATE_ONLINE ? "ONLINE" : + f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN", + JOURNAL_HEADER_SEALED(f->header) ? " SEALED" : "", + (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_ANY) ? " ???" : "", + JOURNAL_HEADER_COMPRESSED_XZ(f->header) ? " COMPRESSED-XZ" : "", + JOURNAL_HEADER_COMPRESSED_LZ4(f->header) ? " COMPRESSED-LZ4" : "", + (le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_ANY) ? " ???" : "", + le64toh(f->header->header_size), + le64toh(f->header->arena_size), + le64toh(f->header->data_hash_table_size) / sizeof(HashItem), + le64toh(f->header->field_hash_table_size) / sizeof(HashItem), + yes_no(journal_file_rotate_suggested(f, 0)), + le64toh(f->header->head_entry_seqnum), le64toh(f->header->head_entry_seqnum), + le64toh(f->header->tail_entry_seqnum), le64toh(f->header->tail_entry_seqnum), + format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)), le64toh(f->header->head_entry_realtime), + format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), le64toh(f->header->tail_entry_realtime), + format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC), le64toh(f->header->tail_entry_monotonic), + le64toh(f->header->n_objects), + le64toh(f->header->n_entries)); + + if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) + printf("Data Objects: %"PRIu64"\n" + "Data Hash Table Fill: %.1f%%\n", + le64toh(f->header->n_data), + 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)))); + + if (JOURNAL_HEADER_CONTAINS(f->header, n_fields)) + printf("Field Objects: %"PRIu64"\n" + "Field Hash Table Fill: %.1f%%\n", + le64toh(f->header->n_fields), + 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)))); + + if (JOURNAL_HEADER_CONTAINS(f->header, n_tags)) + printf("Tag Objects: %"PRIu64"\n", + le64toh(f->header->n_tags)); + if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) + printf("Entry Array Objects: %"PRIu64"\n", + le64toh(f->header->n_entry_arrays)); + + if (fstat(f->fd, &st) >= 0) + printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (uint64_t) st.st_blocks * 512ULL)); +} + +static int journal_file_warn_btrfs(JournalFile *f) { + unsigned attrs; + int r; + + assert(f); + + /* Before we write anything, check if the COW logic is turned + * off on btrfs. Given our write pattern that is quite + * unfriendly to COW file systems this should greatly improve + * performance on COW file systems, such as btrfs, at the + * expense of data integrity features (which shouldn't be too + * bad, given that we do our own checksumming). */ + + r = btrfs_is_filesystem(f->fd); + if (r < 0) + return log_warning_errno(r, "Failed to determine if journal is on btrfs: %m"); + if (!r) + return 0; + + r = read_attr_fd(f->fd, &attrs); + if (r < 0) + return log_warning_errno(r, "Failed to read file attributes: %m"); + + if (attrs & FS_NOCOW_FL) { + log_debug("Detected btrfs file system with copy-on-write disabled, all is good."); + return 0; + } + + log_notice("Creating journal file %s on a btrfs file system, and copy-on-write is enabled. " + "This is likely to slow down journal access substantially, please consider turning " + "off the copy-on-write file attribute on the journal directory, using chattr +C.", f->path); + + return 1; +} + +int journal_file_open( + int fd, + const char *fname, + int flags, + mode_t mode, + bool compress, + bool seal, + JournalMetrics *metrics, + MMapCache *mmap_cache, + Set *deferred_closes, + JournalFile *template, + JournalFile **ret) { + + bool newly_created = false; + JournalFile *f; + void *h; + int r; + + assert(ret); + assert(fd >= 0 || fname); + + if ((flags & O_ACCMODE) != O_RDONLY && + (flags & O_ACCMODE) != O_RDWR) + return -EINVAL; + + if (fname) { + if (!endswith(fname, ".journal") && + !endswith(fname, ".journal~")) + return -EINVAL; + } + + f = new0(JournalFile, 1); + if (!f) + return -ENOMEM; + + f->fd = fd; + f->mode = mode; + + f->flags = flags; + f->prot = prot_from_flags(flags); + f->writable = (flags & O_ACCMODE) != O_RDONLY; +#if defined(HAVE_LZ4) + f->compress_lz4 = compress; +#elif defined(HAVE_XZ) + f->compress_xz = compress; +#endif +#ifdef HAVE_GCRYPT + f->seal = seal; +#endif + + if (mmap_cache) + f->mmap = mmap_cache_ref(mmap_cache); + else { + f->mmap = mmap_cache_new(); + if (!f->mmap) { + r = -ENOMEM; + goto fail; + } + } + + if (fname) + f->path = strdup(fname); + else /* If we don't know the path, fill in something explanatory and vaguely useful */ + asprintf(&f->path, "/proc/self/%i", fd); + if (!f->path) { + r = -ENOMEM; + goto fail; + } + + f->chain_cache = ordered_hashmap_new(&uint64_hash_ops); + if (!f->chain_cache) { + r = -ENOMEM; + goto fail; + } + + if (f->fd < 0) { + f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); + if (f->fd < 0) { + r = -errno; + goto fail; + } + + /* fds we opened here by us should also be closed by us. */ + f->close_fd = true; + } + + r = journal_file_fstat(f); + if (r < 0) + goto fail; + + if (f->last_stat.st_size == 0 && f->writable) { + + (void) journal_file_warn_btrfs(f); + + /* Let's attach the creation time to the journal file, + * so that the vacuuming code knows the age of this + * file even if the file might end up corrupted one + * day... Ideally we'd just use the creation time many + * file systems maintain for each file, but there is + * currently no usable API to query this, hence let's + * emulate this via extended attributes. If extended + * attributes are not supported we'll just skip this, + * and rely solely on mtime/atime/ctime of the file. */ + + fd_setcrtime(f->fd, 0); + +#ifdef HAVE_GCRYPT + /* Try to load the FSPRG state, and if we can't, then + * just don't do sealing */ + if (f->seal) { + r = journal_file_fss_load(f); + if (r < 0) + f->seal = false; + } +#endif + + r = journal_file_init_header(f, template); + if (r < 0) + goto fail; + + r = journal_file_fstat(f); + if (r < 0) + goto fail; + + newly_created = true; + } + + if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) { + r = -ENODATA; + goto fail; + } + + r = mmap_cache_get(f->mmap, f->fd, f->prot, CONTEXT_HEADER, true, 0, PAGE_ALIGN(sizeof(Header)), &f->last_stat, &h); + if (r < 0) + goto fail; + + f->header = h; + + if (!newly_created) { + if (deferred_closes) + journal_file_close_set(deferred_closes); + + r = journal_file_verify_header(f); + if (r < 0) + goto fail; + } + +#ifdef HAVE_GCRYPT + if (!newly_created && f->writable) { + r = journal_file_fss_load(f); + if (r < 0) + goto fail; + } +#endif + + if (f->writable) { + if (metrics) { + journal_default_metrics(metrics, f->fd); + f->metrics = *metrics; + } else if (template) + f->metrics = template->metrics; + + r = journal_file_refresh_header(f); + if (r < 0) + goto fail; + } + +#ifdef HAVE_GCRYPT + r = journal_file_hmac_setup(f); + if (r < 0) + goto fail; +#endif + + if (newly_created) { + r = journal_file_setup_field_hash_table(f); + if (r < 0) + goto fail; + + r = journal_file_setup_data_hash_table(f); + if (r < 0) + goto fail; + +#ifdef HAVE_GCRYPT + r = journal_file_append_first_tag(f); + if (r < 0) + goto fail; +#endif + } + + if (mmap_cache_got_sigbus(f->mmap, f->fd)) { + r = -EIO; + goto fail; + } + + if (template && template->post_change_timer) { + r = journal_file_enable_post_change_timer( + f, + sd_event_source_get_event(template->post_change_timer), + template->post_change_timer_period); + + if (r < 0) + goto fail; + } + + /* The file is opened now successfully, thus we take possession of any passed in fd. */ + f->close_fd = true; + + *ret = f; + return 0; + +fail: + if (f->fd >= 0 && mmap_cache_got_sigbus(f->mmap, f->fd)) + r = -EIO; + + (void) journal_file_close(f); + + return r; +} + +int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred_closes) { + _cleanup_free_ char *p = NULL; + size_t l; + JournalFile *old_file, *new_file = NULL; + int r; + + assert(f); + assert(*f); + + old_file = *f; + + if (!old_file->writable) + return -EINVAL; + + /* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse + * rotation, since we don't know the actual path, and couldn't rename the file hence.*/ + if (path_startswith(old_file->path, "/proc/self/fd")) + return -EINVAL; + + if (!endswith(old_file->path, ".journal")) + return -EINVAL; + + l = strlen(old_file->path); + r = asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal", + (int) l - 8, old_file->path, + SD_ID128_FORMAT_VAL(old_file->header->seqnum_id), + le64toh((*f)->header->head_entry_seqnum), + le64toh((*f)->header->head_entry_realtime)); + if (r < 0) + return -ENOMEM; + + /* Try to rename the file to the archived version. If the file + * already was deleted, we'll get ENOENT, let's ignore that + * case. */ + r = rename(old_file->path, p); + if (r < 0 && errno != ENOENT) + return -errno; + + /* Sync the rename to disk */ + (void) fsync_directory_of_file(old_file->fd); + + /* Set as archive so offlining commits w/state=STATE_ARCHIVED. + * Previously we would set old_file->header->state to STATE_ARCHIVED directly here, + * but journal_file_set_offline() short-circuits when state != STATE_ONLINE, which + * would result in the rotated journal never getting fsync() called before closing. + * Now we simply queue the archive state by setting an archive bit, leaving the state + * as STATE_ONLINE so proper offlining occurs. */ + old_file->archive = true; + + /* Currently, btrfs is not very good with out write patterns + * and fragments heavily. Let's defrag our journal files when + * we archive them */ + old_file->defrag_on_close = true; + + r = journal_file_open(-1, old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, deferred_closes, old_file, &new_file); + + if (deferred_closes && + set_put(deferred_closes, old_file) >= 0) + (void) journal_file_set_offline(old_file, false); + else + (void) journal_file_close(old_file); + + *f = new_file; + return r; +} + +int journal_file_open_reliably( + const char *fname, + int flags, + mode_t mode, + bool compress, + bool seal, + JournalMetrics *metrics, + MMapCache *mmap_cache, + Set *deferred_closes, + JournalFile *template, + JournalFile **ret) { + + int r; + size_t l; + _cleanup_free_ char *p = NULL; + + r = journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); + if (!IN_SET(r, + -EBADMSG, /* corrupted */ + -ENODATA, /* truncated */ + -EHOSTDOWN, /* other machine */ + -EPROTONOSUPPORT, /* incompatible feature */ + -EBUSY, /* unclean shutdown */ + -ESHUTDOWN, /* already archived */ + -EIO, /* IO error, including SIGBUS on mmap */ + -EIDRM /* File has been deleted */)) + return r; + + if ((flags & O_ACCMODE) == O_RDONLY) + return r; + + if (!(flags & O_CREAT)) + return r; + + if (!endswith(fname, ".journal")) + return r; + + /* The file is corrupted. Rotate it away and try it again (but only once) */ + + l = strlen(fname); + if (asprintf(&p, "%.*s@%016"PRIx64 "-%016"PRIx64 ".journal~", + (int) l - 8, fname, + now(CLOCK_REALTIME), + random_u64()) < 0) + return -ENOMEM; + + if (rename(fname, p) < 0) + return -errno; + + /* btrfs doesn't cope well with our write pattern and + * fragments heavily. Let's defrag all files we rotate */ + + (void) chattr_path(p, 0, FS_NOCOW_FL); + (void) btrfs_defrag(p); + + log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname); + + return journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); +} + +int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) { + uint64_t i, n; + uint64_t q, xor_hash = 0; + int r; + EntryItem *items; + dual_timestamp ts; + + assert(from); + assert(to); + assert(o); + assert(p); + + if (!to->writable) + return -EPERM; + + ts.monotonic = le64toh(o->entry.monotonic); + ts.realtime = le64toh(o->entry.realtime); + + n = journal_file_entry_n_items(o); + /* alloca() can't take 0, hence let's allocate at least one */ + items = alloca(sizeof(EntryItem) * MAX(1u, n)); + + for (i = 0; i < n; i++) { + uint64_t l, h; + le64_t le_hash; + size_t t; + void *data; + Object *u; + + q = le64toh(o->entry.items[i].object_offset); + le_hash = o->entry.items[i].hash; + + r = journal_file_move_to_object(from, OBJECT_DATA, q, &o); + if (r < 0) + return r; + + if (le_hash != o->data.hash) + return -EBADMSG; + + l = le64toh(o->object.size) - offsetof(Object, data.payload); + t = (size_t) l; + + /* We hit the limit on 32bit machines */ + if ((uint64_t) t != l) + return -E2BIG; + + if (o->object.flags & OBJECT_COMPRESSION_MASK) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + size_t rsize = 0; + + r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK, + o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize, 0); + if (r < 0) + return r; + + data = from->compress_buffer; + l = rsize; +#else + return -EPROTONOSUPPORT; +#endif + } else + data = o->data.payload; + + r = journal_file_append_data(to, data, l, &u, &h); + if (r < 0) + return r; + + xor_hash ^= le64toh(u->data.hash); + items[i].object_offset = htole64(h); + items[i].hash = u->data.hash; + + r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + } + + r = journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset); + + if (mmap_cache_got_sigbus(to->mmap, to->fd)) + return -EIO; + + return r; +} + +void journal_reset_metrics(JournalMetrics *m) { + assert(m); + + /* Set everything to "pick automatic values". */ + + *m = (JournalMetrics) { + .min_use = (uint64_t) -1, + .max_use = (uint64_t) -1, + .min_size = (uint64_t) -1, + .max_size = (uint64_t) -1, + .keep_free = (uint64_t) -1, + .n_max_files = (uint64_t) -1, + }; +} + +void journal_default_metrics(JournalMetrics *m, int fd) { + char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX], e[FORMAT_BYTES_MAX]; + struct statvfs ss; + uint64_t fs_size; + + assert(m); + assert(fd >= 0); + + if (fstatvfs(fd, &ss) >= 0) + fs_size = ss.f_frsize * ss.f_blocks; + else { + log_debug_errno(errno, "Failed to detremine disk size: %m"); + fs_size = 0; + } + + if (m->max_use == (uint64_t) -1) { + + if (fs_size > 0) { + m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */ + + if (m->max_use > DEFAULT_MAX_USE_UPPER) + m->max_use = DEFAULT_MAX_USE_UPPER; + + if (m->max_use < DEFAULT_MAX_USE_LOWER) + m->max_use = DEFAULT_MAX_USE_LOWER; + } else + m->max_use = DEFAULT_MAX_USE_LOWER; + } else { + m->max_use = PAGE_ALIGN(m->max_use); + + if (m->max_use != 0 && m->max_use < JOURNAL_FILE_SIZE_MIN*2) + m->max_use = JOURNAL_FILE_SIZE_MIN*2; + } + + if (m->min_use == (uint64_t) -1) + m->min_use = DEFAULT_MIN_USE; + + if (m->min_use > m->max_use) + m->min_use = m->max_use; + + if (m->max_size == (uint64_t) -1) { + m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */ + + if (m->max_size > DEFAULT_MAX_SIZE_UPPER) + m->max_size = DEFAULT_MAX_SIZE_UPPER; + } else + m->max_size = PAGE_ALIGN(m->max_size); + + if (m->max_size != 0) { + if (m->max_size < JOURNAL_FILE_SIZE_MIN) + m->max_size = JOURNAL_FILE_SIZE_MIN; + + if (m->max_use != 0 && m->max_size*2 > m->max_use) + m->max_use = m->max_size*2; + } + + if (m->min_size == (uint64_t) -1) + m->min_size = JOURNAL_FILE_SIZE_MIN; + else { + m->min_size = PAGE_ALIGN(m->min_size); + + if (m->min_size < JOURNAL_FILE_SIZE_MIN) + m->min_size = JOURNAL_FILE_SIZE_MIN; + + if (m->max_size != 0 && m->min_size > m->max_size) + m->max_size = m->min_size; + } + + if (m->keep_free == (uint64_t) -1) { + + if (fs_size > 0) { + m->keep_free = PAGE_ALIGN(fs_size * 3 / 20); /* 15% of file system size */ + + if (m->keep_free > DEFAULT_KEEP_FREE_UPPER) + m->keep_free = DEFAULT_KEEP_FREE_UPPER; + + } else + m->keep_free = DEFAULT_KEEP_FREE; + } + + if (m->n_max_files == (uint64_t) -1) + m->n_max_files = DEFAULT_N_MAX_FILES; + + log_debug("Fixed min_use=%s max_use=%s max_size=%s min_size=%s keep_free=%s n_max_files=%" PRIu64, + format_bytes(a, sizeof(a), m->min_use), + format_bytes(b, sizeof(b), m->max_use), + format_bytes(c, sizeof(c), m->max_size), + format_bytes(d, sizeof(d), m->min_size), + format_bytes(e, sizeof(e), m->keep_free), + m->n_max_files); +} + +int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) { + assert(f); + assert(f->header); + assert(from || to); + + if (from) { + if (f->header->head_entry_realtime == 0) + return -ENOENT; + + *from = le64toh(f->header->head_entry_realtime); + } + + if (to) { + if (f->header->tail_entry_realtime == 0) + return -ENOENT; + + *to = le64toh(f->header->tail_entry_realtime); + } + + return 1; +} + +int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) { + Object *o; + uint64_t p; + int r; + + assert(f); + assert(from || to); + + r = find_data_object_by_boot_id(f, boot_id, &o, &p); + if (r <= 0) + return r; + + if (le64toh(o->data.n_entries) <= 0) + return 0; + + if (from) { + r = journal_file_move_to_object(f, OBJECT_ENTRY, le64toh(o->data.entry_offset), &o); + if (r < 0) + return r; + + *from = le64toh(o->entry.monotonic); + } + + if (to) { + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + r = generic_array_get_plus_one(f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries)-1, + &o, NULL); + if (r <= 0) + return r; + + *to = le64toh(o->entry.monotonic); + } + + return 1; +} + +bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) { + assert(f); + assert(f->header); + + /* If we gained new header fields we gained new features, + * hence suggest a rotation */ + if (le64toh(f->header->header_size) < sizeof(Header)) { + log_debug("%s uses an outdated header, suggesting rotation.", f->path); + return true; + } + + /* Let's check if the hash tables grew over a certain fill + * level (75%, borrowing this value from Java's hash table + * implementation), and if so suggest a rotation. To calculate + * the fill level we need the n_data field, which only exists + * in newer versions. */ + + if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) + if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) { + log_debug("Data hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items, %llu file size, %"PRIu64" bytes per hash table item), suggesting rotation.", + f->path, + 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))), + le64toh(f->header->n_data), + le64toh(f->header->data_hash_table_size) / sizeof(HashItem), + (unsigned long long) f->last_stat.st_size, + f->last_stat.st_size / le64toh(f->header->n_data)); + return true; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_fields)) + if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) { + log_debug("Field hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items), suggesting rotation.", + f->path, + 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))), + le64toh(f->header->n_fields), + le64toh(f->header->field_hash_table_size) / sizeof(HashItem)); + return true; + } + + /* Are the data objects properly indexed by field objects? */ + if (JOURNAL_HEADER_CONTAINS(f->header, n_data) && + JOURNAL_HEADER_CONTAINS(f->header, n_fields) && + le64toh(f->header->n_data) > 0 && + le64toh(f->header->n_fields) == 0) + return true; + + if (max_file_usec > 0) { + usec_t t, h; + + h = le64toh(f->header->head_entry_realtime); + t = now(CLOCK_REALTIME); + + if (h > 0 && t > h + max_file_usec) + return true; + } + + return false; +} diff --git a/src/libsystemd/src/sd-journal/journal-file.h b/src/libsystemd/src/sd-journal/journal-file.h new file mode 100644 index 0000000000..e48e98f424 --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-file.h @@ -0,0 +1,265 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include + +#ifdef HAVE_GCRYPT +#include +#endif + +#include + +#include "hashmap.h" +#include "journal-def.h" +#include "macro.h" +#include "mmap-cache.h" +#include +#include "sparse-endian.h" + +typedef struct JournalMetrics { + /* For all these: -1 means "pick automatically", and 0 means "no limit enforced" */ + uint64_t max_size; /* how large journal files grow at max */ + uint64_t min_size; /* how large journal files grow at least */ + uint64_t max_use; /* how much disk space to use in total at max, keep_free permitting */ + uint64_t min_use; /* how much disk space to use in total at least, even if keep_free says not to */ + uint64_t keep_free; /* how much to keep free on disk */ + uint64_t n_max_files; /* how many files to keep around at max */ +} JournalMetrics; + +typedef enum direction { + DIRECTION_UP, + DIRECTION_DOWN +} direction_t; + +typedef enum LocationType { + /* The first and last entries, resp. */ + LOCATION_HEAD, + LOCATION_TAIL, + + /* We already read the entry we currently point to, and the + * next one to read should probably not be this one again. */ + LOCATION_DISCRETE, + + /* We should seek to the precise location specified, and + * return it, as we haven't read it yet. */ + LOCATION_SEEK +} LocationType; + +typedef enum OfflineState { + OFFLINE_JOINED, + OFFLINE_SYNCING, + OFFLINE_OFFLINING, + OFFLINE_CANCEL, + OFFLINE_AGAIN_FROM_SYNCING, + OFFLINE_AGAIN_FROM_OFFLINING, + OFFLINE_DONE +} OfflineState; + +typedef struct JournalFile { + int fd; + + mode_t mode; + + int flags; + int prot; + bool writable:1; + bool compress_xz:1; + bool compress_lz4:1; + bool seal:1; + bool defrag_on_close:1; + bool close_fd:1; + bool archive:1; + + bool tail_entry_monotonic_valid:1; + + direction_t last_direction; + LocationType location_type; + uint64_t last_n_entries; + + char *path; + struct stat last_stat; + usec_t last_stat_usec; + + Header *header; + HashItem *data_hash_table; + HashItem *field_hash_table; + + uint64_t current_offset; + uint64_t current_seqnum; + uint64_t current_realtime; + uint64_t current_monotonic; + sd_id128_t current_boot_id; + uint64_t current_xor_hash; + + JournalMetrics metrics; + MMapCache *mmap; + + sd_event_source *post_change_timer; + usec_t post_change_timer_period; + + OrderedHashmap *chain_cache; + + pthread_t offline_thread; + volatile OfflineState offline_state; + +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + void *compress_buffer; + size_t compress_buffer_size; +#endif + +#ifdef HAVE_GCRYPT + gcry_md_hd_t hmac; + bool hmac_running; + + FSSHeader *fss_file; + size_t fss_file_size; + + uint64_t fss_start_usec; + uint64_t fss_interval_usec; + + void *fsprg_state; + size_t fsprg_state_size; + + void *fsprg_seed; + size_t fsprg_seed_size; +#endif +} JournalFile; + +int journal_file_open( + int fd, + const char *fname, + int flags, + mode_t mode, + bool compress, + bool seal, + JournalMetrics *metrics, + MMapCache *mmap_cache, + Set *deferred_closes, + JournalFile *template, + JournalFile **ret); + +int journal_file_set_offline(JournalFile *f, bool wait); +bool journal_file_is_offlining(JournalFile *f); +JournalFile* journal_file_close(JournalFile *j); +void journal_file_close_set(Set *s); + +int journal_file_open_reliably( + const char *fname, + int flags, + mode_t mode, + bool compress, + bool seal, + JournalMetrics *metrics, + MMapCache *mmap_cache, + Set *deferred_closes, + JournalFile *template, + JournalFile **ret); + +#define ALIGN64(x) (((x) + 7ULL) & ~7ULL) +#define VALID64(x) (((x) & 7ULL) == 0ULL) + +/* Use six characters to cover the offsets common in smallish journal + * files without adding too many zeros. */ +#define OFSfmt "%06"PRIx64 + +static inline bool VALID_REALTIME(uint64_t u) { + /* This considers timestamps until the year 3112 valid. That should be plenty room... */ + return u > 0 && u < (1ULL << 55); +} + +static inline bool VALID_MONOTONIC(uint64_t u) { + /* This considers timestamps until 1142 years of runtime valid. */ + return u < (1ULL << 55); +} + +static inline bool VALID_EPOCH(uint64_t u) { + /* This allows changing the key for 1142 years, every usec. */ + return u < (1ULL << 55); +} + +#define JOURNAL_HEADER_CONTAINS(h, field) \ + (le64toh((h)->header_size) >= offsetof(Header, field) + sizeof((h)->field)) + +#define JOURNAL_HEADER_SEALED(h) \ + (!!(le32toh((h)->compatible_flags) & HEADER_COMPATIBLE_SEALED)) + +#define JOURNAL_HEADER_COMPRESSED_XZ(h) \ + (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED_XZ)) + +#define JOURNAL_HEADER_COMPRESSED_LZ4(h) \ + (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED_LZ4)) + +int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret); + +uint64_t journal_file_entry_n_items(Object *o) _pure_; +uint64_t journal_file_entry_array_n_items(Object *o) _pure_; +uint64_t journal_file_hash_table_n_items(Object *o) _pure_; + +int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset); +int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, uint64_t *offset); + +int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); +int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset); + +int journal_file_find_field_object(JournalFile *f, const void *field, uint64_t size, Object **ret, uint64_t *offset); +int journal_file_find_field_object_with_hash(JournalFile *f, const void *field, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset); + +void journal_file_reset_location(JournalFile *f); +void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset); +int journal_file_compare_locations(JournalFile *af, JournalFile *bf); +int journal_file_next_entry(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_next_entry_for_data(JournalFile *f, Object *o, uint64_t p, uint64_t data_offset, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_move_to_entry_by_offset_for_data(JournalFile *f, uint64_t data_offset, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_seqnum_for_data(JournalFile *f, uint64_t data_offset, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_realtime_for_data(JournalFile *f, uint64_t data_offset, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_monotonic_for_data(JournalFile *f, uint64_t data_offset, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset); + +void journal_file_dump(JournalFile *f); +void journal_file_print_header(JournalFile *f); + +int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred_closes); + +void journal_file_post_change(JournalFile *f); +int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t); + +void journal_reset_metrics(JournalMetrics *m); +void journal_default_metrics(JournalMetrics *m, int fd); + +int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to); +int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot, usec_t *from, usec_t *to); + +bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec); + +int journal_file_map_data_hash_table(JournalFile *f); +int journal_file_map_field_hash_table(JournalFile *f); + +static inline bool JOURNAL_FILE_COMPRESS(JournalFile *f) { + assert(f); + return f->compress_xz || f->compress_lz4; +} diff --git a/src/libsystemd/src/sd-journal/journal-internal.h b/src/libsystemd/src/sd-journal/journal-internal.h new file mode 100644 index 0000000000..d8eb11ad06 --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-internal.h @@ -0,0 +1,143 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include +#include + +#include +#include + +#include "hashmap.h" +#include "journal-def.h" +#include "journal-file.h" +#include "list.h" +#include "set.h" + +typedef struct Match Match; +typedef struct Location Location; +typedef struct Directory Directory; + +typedef enum MatchType { + MATCH_DISCRETE, + MATCH_OR_TERM, + MATCH_AND_TERM +} MatchType; + +struct Match { + MatchType type; + Match *parent; + LIST_FIELDS(Match, matches); + + /* For concrete matches */ + char *data; + size_t size; + le64_t le_hash; + + /* For terms */ + LIST_HEAD(Match, matches); +}; + +struct Location { + LocationType type; + + bool seqnum_set; + bool realtime_set; + bool monotonic_set; + bool xor_hash_set; + + uint64_t seqnum; + sd_id128_t seqnum_id; + + uint64_t realtime; + + uint64_t monotonic; + sd_id128_t boot_id; + + uint64_t xor_hash; +}; + +struct Directory { + char *path; + int wd; + bool is_root; +}; + +struct sd_journal { + int toplevel_fd; + + char *path; + char *prefix; + + OrderedHashmap *files; + MMapCache *mmap; + + Location current_location; + + JournalFile *current_file; + uint64_t current_field; + + Match *level0, *level1, *level2; + + pid_t original_pid; + + int inotify_fd; + unsigned current_invalidate_counter, last_invalidate_counter; + usec_t last_process_usec; + + /* Iterating through unique fields and their data values */ + char *unique_field; + JournalFile *unique_file; + uint64_t unique_offset; + + /* Iterating through known fields */ + JournalFile *fields_file; + uint64_t fields_offset; + uint64_t fields_hash_table_index; + char *fields_buffer; + size_t fields_buffer_allocated; + + int flags; + + bool on_network:1; + bool no_new_files:1; + bool no_inotify:1; + bool unique_file_lost:1; /* File we were iterating over got + removed, and there were no more + files, so sd_j_enumerate_unique + will return a value equal to 0. */ + bool fields_file_lost:1; + bool has_runtime_files:1; + bool has_persistent_files:1; + + size_t data_threshold; + + Hashmap *directories_by_path; + Hashmap *directories_by_wd; + + Hashmap *errors; +}; + +char *journal_make_match_string(sd_journal *j); +void journal_print_header(sd_journal *j); + +#define JOURNAL_FOREACH_DATA_RETVAL(j, data, l, retval) \ + for (sd_journal_restart_data(j); ((retval) = sd_journal_enumerate_data((j), &(data), &(l))) > 0; ) diff --git a/src/libsystemd/src/sd-journal/journal-send.c b/src/libsystemd/src/sd-journal/journal-send.c new file mode 100644 index 0000000000..5a49211f85 --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-send.c @@ -0,0 +1,559 @@ +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#define SD_JOURNAL_SUPPRESS_LOCATION + +#include + +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "io-util.h" +#include "memfd-util.h" +#include "socket-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "util.h" + +#define SNDBUF_SIZE (8*1024*1024) + +#define ALLOCA_CODE_FUNC(f, func) \ + do { \ + size_t _fl; \ + const char *_func = (func); \ + char **_f = &(f); \ + _fl = strlen(_func) + 1; \ + *_f = alloca(_fl + 10); \ + memcpy(*_f, "CODE_FUNC=", 10); \ + memcpy(*_f + 10, _func, _fl); \ + } while (false) + +/* We open a single fd, and we'll share it with the current process, + * all its threads, and all its subprocesses. This means we need to + * initialize it atomically, and need to operate on it atomically + * never assuming we are the only user */ + +static int journal_fd(void) { + int fd; + static int fd_plus_one = 0; + +retry: + if (fd_plus_one > 0) + return fd_plus_one - 1; + + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (fd < 0) + return -errno; + + fd_inc_sndbuf(fd, SNDBUF_SIZE); + + if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) { + safe_close(fd); + goto retry; + } + + return fd; +} + +_public_ int sd_journal_print(int priority, const char *format, ...) { + int r; + va_list ap; + + va_start(ap, format); + r = sd_journal_printv(priority, format, ap); + va_end(ap); + + return r; +} + +_public_ int sd_journal_printv(int priority, const char *format, va_list ap) { + + /* FIXME: Instead of limiting things to LINE_MAX we could do a + C99 variable-length array on the stack here in a loop. */ + + char buffer[8 + LINE_MAX], p[sizeof("PRIORITY=")-1 + DECIMAL_STR_MAX(int) + 1]; + struct iovec iov[2]; + + assert_return(priority >= 0, -EINVAL); + assert_return(priority <= 7, -EINVAL); + assert_return(format, -EINVAL); + + xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK); + + memcpy(buffer, "MESSAGE=", 8); + vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); + + zero(iov); + IOVEC_SET_STRING(iov[0], buffer); + IOVEC_SET_STRING(iov[1], p); + + return sd_journal_sendv(iov, 2); +} + +_printf_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) { + PROTECT_ERRNO; + int r, n = 0, i = 0, j; + struct iovec *iov = NULL; + + assert(_iov); + + if (extra > 0) { + n = MAX(extra * 2, extra + 4); + iov = malloc0(n * sizeof(struct iovec)); + if (!iov) { + r = -ENOMEM; + goto fail; + } + + i = extra; + } + + while (format) { + struct iovec *c; + char *buffer; + va_list aq; + + if (i >= n) { + n = MAX(i*2, 4); + c = realloc(iov, n * sizeof(struct iovec)); + if (!c) { + r = -ENOMEM; + goto fail; + } + + iov = c; + } + + va_copy(aq, ap); + if (vasprintf(&buffer, format, aq) < 0) { + va_end(aq); + r = -ENOMEM; + goto fail; + } + va_end(aq); + + VA_FORMAT_ADVANCE(format, ap); + + IOVEC_SET_STRING(iov[i++], buffer); + + format = va_arg(ap, char *); + } + + *_iov = iov; + + return i; + +fail: + for (j = 0; j < i; j++) + free(iov[j].iov_base); + + free(iov); + + return r; +} + +_public_ int sd_journal_send(const char *format, ...) { + int r, i, j; + va_list ap; + struct iovec *iov = NULL; + + va_start(ap, format); + i = fill_iovec_sprintf(format, ap, 0, &iov); + va_end(ap); + + if (_unlikely_(i < 0)) { + r = i; + goto finish; + } + + r = sd_journal_sendv(iov, i); + +finish: + for (j = 0; j < i; j++) + free(iov[j].iov_base); + + free(iov); + + return r; +} + +_public_ int sd_journal_sendv(const struct iovec *iov, int n) { + PROTECT_ERRNO; + int fd, r; + _cleanup_close_ int buffer_fd = -1; + struct iovec *w; + uint64_t *l; + int i, j = 0; + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/socket", + }; + struct msghdr mh = { + .msg_name = (struct sockaddr*) &sa.sa, + .msg_namelen = SOCKADDR_UN_LEN(sa.un), + }; + ssize_t k; + bool have_syslog_identifier = false; + bool seal = true; + + assert_return(iov, -EINVAL); + assert_return(n > 0, -EINVAL); + + w = newa(struct iovec, n * 5 + 3); + l = newa(uint64_t, n); + + for (i = 0; i < n; i++) { + char *c, *nl; + + if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1)) + return -EINVAL; + + c = memchr(iov[i].iov_base, '=', iov[i].iov_len); + if (_unlikely_(!c || c == iov[i].iov_base)) + return -EINVAL; + + have_syslog_identifier = have_syslog_identifier || + (c == (char *) iov[i].iov_base + 17 && + startswith(iov[i].iov_base, "SYSLOG_IDENTIFIER")); + + nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len); + if (nl) { + if (_unlikely_(nl < c)) + return -EINVAL; + + /* Already includes a newline? Bummer, then + * let's write the variable name, then a + * newline, then the size (64bit LE), followed + * by the data and a final newline */ + + w[j].iov_base = iov[i].iov_base; + w[j].iov_len = c - (char*) iov[i].iov_base; + j++; + + IOVEC_SET_STRING(w[j++], "\n"); + + l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1); + w[j].iov_base = &l[i]; + w[j].iov_len = sizeof(uint64_t); + j++; + + w[j].iov_base = c + 1; + w[j].iov_len = iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1; + j++; + + } else + /* Nothing special? Then just add the line and + * append a newline */ + w[j++] = iov[i]; + + IOVEC_SET_STRING(w[j++], "\n"); + } + + if (!have_syslog_identifier && + string_is_safe(program_invocation_short_name)) { + + /* Implicitly add program_invocation_short_name, if it + * is not set explicitly. We only do this for + * program_invocation_short_name, and nothing else + * since everything else is much nicer to retrieve + * from the outside. */ + + IOVEC_SET_STRING(w[j++], "SYSLOG_IDENTIFIER="); + IOVEC_SET_STRING(w[j++], program_invocation_short_name); + IOVEC_SET_STRING(w[j++], "\n"); + } + + fd = journal_fd(); + if (_unlikely_(fd < 0)) + return fd; + + mh.msg_iov = w; + mh.msg_iovlen = j; + + k = sendmsg(fd, &mh, MSG_NOSIGNAL); + if (k >= 0) + return 0; + + /* Fail silently if the journal is not available */ + if (errno == ENOENT) + return 0; + + if (errno != EMSGSIZE && errno != ENOBUFS) + return -errno; + + /* Message doesn't fit... Let's dump the data in a memfd or + * temporary file and just pass a file descriptor of it to the + * other side. + * + * For the temporary files we use /dev/shm instead of /tmp + * here, since we want this to be a tmpfs, and one that is + * available from early boot on and where unprivileged users + * can create files. */ + buffer_fd = memfd_new(NULL); + if (buffer_fd < 0) { + if (buffer_fd == -ENOSYS) { + buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC); + if (buffer_fd < 0) + return buffer_fd; + + seal = false; + } else + return buffer_fd; + } + + n = writev(buffer_fd, w, j); + if (n < 0) + return -errno; + + if (seal) { + r = memfd_set_sealed(buffer_fd); + if (r < 0) + return r; + } + + r = send_one_fd_sa(fd, buffer_fd, mh.msg_name, mh.msg_namelen, 0); + if (r == -ENOENT) + /* Fail silently if the journal is not available */ + return 0; + return r; +} + +static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) { + PROTECT_ERRNO; + size_t n, k; + + k = isempty(message) ? 0 : strlen(message) + 2; + n = 8 + k + 256 + 1; + + for (;;) { + char buffer[n]; + char* j; + + errno = 0; + j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k); + if (errno == 0) { + char error[sizeof("ERRNO=")-1 + DECIMAL_STR_MAX(int) + 1]; + + if (j != buffer + 8 + k) + memmove(buffer + 8 + k, j, strlen(j)+1); + + memcpy(buffer, "MESSAGE=", 8); + + if (k > 0) { + memcpy(buffer + 8, message, k - 2); + memcpy(buffer + 8 + k - 2, ": ", 2); + } + + xsprintf(error, "ERRNO=%i", _saved_errno_); + + assert_cc(3 == LOG_ERR); + IOVEC_SET_STRING(iov[skip+0], "PRIORITY=3"); + IOVEC_SET_STRING(iov[skip+1], buffer); + IOVEC_SET_STRING(iov[skip+2], error); + + return sd_journal_sendv(iov, skip + 3); + } + + if (errno != ERANGE) + return -errno; + + n *= 2; + } +} + +_public_ int sd_journal_perror(const char *message) { + struct iovec iovec[3]; + + return fill_iovec_perror_and_send(message, 0, iovec); +} + +_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) { + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/stdout", + }; + _cleanup_close_ int fd = -1; + char *header; + size_t l; + int r; + + assert_return(priority >= 0, -EINVAL); + assert_return(priority <= 7, -EINVAL); + + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + if (fd < 0) + return -errno; + + r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + return -errno; + + if (shutdown(fd, SHUT_RD) < 0) + return -errno; + + fd_inc_sndbuf(fd, SNDBUF_SIZE); + + if (!identifier) + identifier = ""; + + l = strlen(identifier); + header = alloca(l + 1 + 1 + 2 + 2 + 2 + 2 + 2); + + memcpy(header, identifier, l); + header[l++] = '\n'; + header[l++] = '\n'; /* unit id */ + header[l++] = '0' + priority; + header[l++] = '\n'; + header[l++] = '0' + !!level_prefix; + header[l++] = '\n'; + header[l++] = '0'; + header[l++] = '\n'; + header[l++] = '0'; + header[l++] = '\n'; + header[l++] = '0'; + header[l++] = '\n'; + + r = loop_write(fd, header, l, false); + if (r < 0) + return r; + + r = fd; + fd = -1; + return r; +} + +_public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) { + int r; + va_list ap; + + va_start(ap, format); + r = sd_journal_printv_with_location(priority, file, line, func, format, ap); + va_end(ap); + + return r; +} + +_public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) { + char buffer[8 + LINE_MAX], p[sizeof("PRIORITY=")-1 + DECIMAL_STR_MAX(int) + 1]; + struct iovec iov[5]; + char *f; + + assert_return(priority >= 0, -EINVAL); + assert_return(priority <= 7, -EINVAL); + assert_return(format, -EINVAL); + + xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK); + + memcpy(buffer, "MESSAGE=", 8); + vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); + + /* func is initialized from __func__ which is not a macro, but + * a static const char[], hence cannot easily be prefixed with + * CODE_FUNC=, hence let's do it manually here. */ + ALLOCA_CODE_FUNC(f, func); + + zero(iov); + IOVEC_SET_STRING(iov[0], buffer); + IOVEC_SET_STRING(iov[1], p); + IOVEC_SET_STRING(iov[2], file); + IOVEC_SET_STRING(iov[3], line); + IOVEC_SET_STRING(iov[4], f); + + return sd_journal_sendv(iov, ELEMENTSOF(iov)); +} + +_public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) { + int r, i, j; + va_list ap; + struct iovec *iov = NULL; + char *f; + + va_start(ap, format); + i = fill_iovec_sprintf(format, ap, 3, &iov); + va_end(ap); + + if (_unlikely_(i < 0)) { + r = i; + goto finish; + } + + ALLOCA_CODE_FUNC(f, func); + + IOVEC_SET_STRING(iov[0], file); + IOVEC_SET_STRING(iov[1], line); + IOVEC_SET_STRING(iov[2], f); + + r = sd_journal_sendv(iov, i); + +finish: + for (j = 3; j < i; j++) + free(iov[j].iov_base); + + free(iov); + + return r; +} + +_public_ int sd_journal_sendv_with_location( + const char *file, const char *line, + const char *func, + const struct iovec *iov, int n) { + + struct iovec *niov; + char *f; + + assert_return(iov, -EINVAL); + assert_return(n > 0, -EINVAL); + + niov = alloca(sizeof(struct iovec) * (n + 3)); + memcpy(niov, iov, sizeof(struct iovec) * n); + + ALLOCA_CODE_FUNC(f, func); + + IOVEC_SET_STRING(niov[n++], file); + IOVEC_SET_STRING(niov[n++], line); + IOVEC_SET_STRING(niov[n++], f); + + return sd_journal_sendv(niov, n); +} + +_public_ int sd_journal_perror_with_location( + const char *file, const char *line, + const char *func, + const char *message) { + + struct iovec iov[6]; + char *f; + + ALLOCA_CODE_FUNC(f, func); + + IOVEC_SET_STRING(iov[0], file); + IOVEC_SET_STRING(iov[1], line); + IOVEC_SET_STRING(iov[2], f); + + return fill_iovec_perror_and_send(message, 3, iov); +} diff --git a/src/libsystemd/src/sd-journal/journal-vacuum.c b/src/libsystemd/src/sd-journal/journal-vacuum.c new file mode 100644 index 0000000000..cd2676ab63 --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-vacuum.c @@ -0,0 +1,349 @@ +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "journal-def.h" +#include "journal-file.h" +#include "journal-vacuum.h" +#include "parse-util.h" +#include "string-util.h" +#include "util.h" +#include "xattr-util.h" + +struct vacuum_info { + uint64_t usage; + char *filename; + + uint64_t realtime; + + sd_id128_t seqnum_id; + uint64_t seqnum; + bool have_seqnum; +}; + +static int vacuum_compare(const void *_a, const void *_b) { + const struct vacuum_info *a, *b; + + a = _a; + b = _b; + + if (a->have_seqnum && b->have_seqnum && + sd_id128_equal(a->seqnum_id, b->seqnum_id)) { + if (a->seqnum < b->seqnum) + return -1; + else if (a->seqnum > b->seqnum) + return 1; + else + return 0; + } + + if (a->realtime < b->realtime) + return -1; + else if (a->realtime > b->realtime) + return 1; + else if (a->have_seqnum && b->have_seqnum) + return memcmp(&a->seqnum_id, &b->seqnum_id, 16); + else + return strcmp(a->filename, b->filename); +} + +static void patch_realtime( + int fd, + const char *fn, + const struct stat *st, + unsigned long long *realtime) { + + usec_t x, crtime = 0; + + /* The timestamp was determined by the file name, but let's + * see if the file might actually be older than the file name + * suggested... */ + + assert(fd >= 0); + assert(fn); + assert(st); + assert(realtime); + + x = timespec_load(&st->st_ctim); + if (x > 0 && x != USEC_INFINITY && x < *realtime) + *realtime = x; + + x = timespec_load(&st->st_atim); + if (x > 0 && x != USEC_INFINITY && x < *realtime) + *realtime = x; + + x = timespec_load(&st->st_mtim); + if (x > 0 && x != USEC_INFINITY && x < *realtime) + *realtime = x; + + /* Let's read the original creation time, if possible. Ideally + * we'd just query the creation time the FS might provide, but + * unfortunately there's currently no sane API to query + * it. Hence let's implement this manually... */ + + if (fd_getcrtime_at(fd, fn, &crtime, 0) >= 0) { + if (crtime < *realtime) + *realtime = crtime; + } +} + +static int journal_file_empty(int dir_fd, const char *name) { + _cleanup_close_ int fd; + struct stat st; + le64_t n_entries; + ssize_t n; + + fd = openat(dir_fd, name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK|O_NOATIME); + if (fd < 0) { + /* Maybe failed due to O_NOATIME and lack of privileges? */ + fd = openat(dir_fd, name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); + if (fd < 0) + return -errno; + } + + if (fstat(fd, &st) < 0) + return -errno; + + /* If an offline file doesn't even have a header we consider it empty */ + if (st.st_size < (off_t) sizeof(Header)) + return 1; + + /* If the number of entries is empty, we consider it empty, too */ + n = pread(fd, &n_entries, sizeof(n_entries), offsetof(Header, n_entries)); + if (n < 0) + return -errno; + if (n != sizeof(n_entries)) + return -EIO; + + return le64toh(n_entries) <= 0; +} + +int journal_directory_vacuum( + const char *directory, + uint64_t max_use, + uint64_t n_max_files, + usec_t max_retention_usec, + usec_t *oldest_usec, + bool verbose) { + + _cleanup_closedir_ DIR *d = NULL; + struct vacuum_info *list = NULL; + unsigned n_list = 0, i, n_active_files = 0; + size_t n_allocated = 0; + uint64_t sum = 0, freed = 0; + usec_t retention_limit = 0; + char sbytes[FORMAT_BYTES_MAX]; + struct dirent *de; + int r; + + assert(directory); + + if (max_use <= 0 && max_retention_usec <= 0 && n_max_files <= 0) + return 0; + + if (max_retention_usec > 0) { + retention_limit = now(CLOCK_REALTIME); + if (retention_limit > max_retention_usec) + retention_limit -= max_retention_usec; + else + max_retention_usec = retention_limit = 0; + } + + d = opendir(directory); + if (!d) + return -errno; + + FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) { + + unsigned long long seqnum = 0, realtime; + _cleanup_free_ char *p = NULL; + sd_id128_t seqnum_id; + bool have_seqnum; + uint64_t size; + struct stat st; + size_t q; + + if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { + log_debug_errno(errno, "Failed to stat file %s while vacuuming, ignoring: %m", de->d_name); + continue; + } + + if (!S_ISREG(st.st_mode)) + continue; + + q = strlen(de->d_name); + + if (endswith(de->d_name, ".journal")) { + + /* Vacuum archived files. Active files are + * left around */ + + if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8) { + n_active_files++; + continue; + } + + if (de->d_name[q-8-16-1] != '-' || + de->d_name[q-8-16-1-16-1] != '-' || + de->d_name[q-8-16-1-16-1-32-1] != '@') { + n_active_files++; + continue; + } + + p = strdup(de->d_name); + if (!p) { + r = -ENOMEM; + goto finish; + } + + de->d_name[q-8-16-1-16-1] = 0; + if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) { + n_active_files++; + continue; + } + + if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) { + n_active_files++; + continue; + } + + have_seqnum = true; + + } else if (endswith(de->d_name, ".journal~")) { + unsigned long long tmp; + + /* Vacuum corrupted files */ + + if (q < 1 + 16 + 1 + 16 + 8 + 1) { + n_active_files++; + continue; + } + + if (de->d_name[q-1-8-16-1] != '-' || + de->d_name[q-1-8-16-1-16-1] != '@') { + n_active_files++; + continue; + } + + p = strdup(de->d_name); + if (!p) { + r = -ENOMEM; + goto finish; + } + + if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) { + n_active_files++; + continue; + } + + have_seqnum = false; + } else { + /* We do not vacuum unknown files! */ + log_debug("Not vacuuming unknown file %s.", de->d_name); + continue; + } + + size = 512UL * (uint64_t) st.st_blocks; + + r = journal_file_empty(dirfd(d), p); + if (r < 0) { + log_debug_errno(r, "Failed check if %s is empty, ignoring: %m", p); + continue; + } + if (r > 0) { + /* Always vacuum empty non-online files. */ + + if (unlinkat(dirfd(d), p, 0) >= 0) { + + log_full(verbose ? LOG_INFO : LOG_DEBUG, + "Deleted empty archived journal %s/%s (%s).", directory, p, format_bytes(sbytes, sizeof(sbytes), size)); + + freed += size; + } else if (errno != ENOENT) + log_warning_errno(errno, "Failed to delete empty archived journal %s/%s: %m", directory, p); + + continue; + } + + patch_realtime(dirfd(d), p, &st, &realtime); + + if (!GREEDY_REALLOC(list, n_allocated, n_list + 1)) { + r = -ENOMEM; + goto finish; + } + + list[n_list].filename = p; + list[n_list].usage = size; + list[n_list].seqnum = seqnum; + list[n_list].realtime = realtime; + list[n_list].seqnum_id = seqnum_id; + list[n_list].have_seqnum = have_seqnum; + n_list++; + + p = NULL; + sum += size; + } + + qsort_safe(list, n_list, sizeof(struct vacuum_info), vacuum_compare); + + for (i = 0; i < n_list; i++) { + unsigned left; + + left = n_active_files + n_list - i; + + if ((max_retention_usec <= 0 || list[i].realtime >= retention_limit) && + (max_use <= 0 || sum <= max_use) && + (n_max_files <= 0 || left <= n_max_files)) + break; + + if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) { + log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted archived journal %s/%s (%s).", directory, list[i].filename, format_bytes(sbytes, sizeof(sbytes), list[i].usage)); + freed += list[i].usage; + + if (list[i].usage < sum) + sum -= list[i].usage; + else + sum = 0; + + } else if (errno != ENOENT) + log_warning_errno(errno, "Failed to delete archived journal %s/%s: %m", directory, list[i].filename); + } + + if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec)) + *oldest_usec = list[i].realtime; + + r = 0; + +finish: + for (i = 0; i < n_list; i++) + free(list[i].filename); + free(list); + + log_full(verbose ? LOG_INFO : LOG_DEBUG, "Vacuuming done, freed %s of archived journals on disk.", format_bytes(sbytes, sizeof(sbytes), freed)); + + return r; +} diff --git a/src/libsystemd/src/sd-journal/journal-vacuum.h b/src/libsystemd/src/sd-journal/journal-vacuum.h new file mode 100644 index 0000000000..1e750a2170 --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-vacuum.h @@ -0,0 +1,27 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include + +#include "time-util.h" + +int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t n_max_files, usec_t max_retention_usec, usec_t *oldest_usec, bool verbose); diff --git a/src/libsystemd/src/sd-journal/journal-verify.c b/src/libsystemd/src/sd-journal/journal-verify.c new file mode 100644 index 0000000000..26572ddd76 --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-verify.c @@ -0,0 +1,1294 @@ +/*** + This file is part of systemd. + + Copyright 2012 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 . +***/ + +#include +#include +#include +#include + +#include "alloc-util.h" +#include "compress.h" +#include "fd-util.h" +#include "fileio.h" +#include "journal-authenticate.h" +#include "journal-def.h" +#include "journal-file.h" +#include "journal-verify.h" +#include "lookup3.h" +#include "macro.h" +#include "terminal-util.h" +#include "util.h" + +static void draw_progress(uint64_t p, usec_t *last_usec) { + unsigned n, i, j, k; + usec_t z, x; + + if (!on_tty()) + return; + + z = now(CLOCK_MONOTONIC); + x = *last_usec; + + if (x != 0 && x + 40 * USEC_PER_MSEC > z) + return; + + *last_usec = z; + + n = (3 * columns()) / 4; + j = (n * (unsigned) p) / 65535ULL; + k = n - j; + + fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN, stdout); + + for (i = 0; i < j; i++) + fputs("\xe2\x96\x88", stdout); + + fputs(ANSI_NORMAL, stdout); + + for (i = 0; i < k; i++) + fputs("\xe2\x96\x91", stdout); + + printf(" %3"PRIu64"%%", 100U * p / 65535U); + + fputs("\r\x1B[?25h", stdout); + fflush(stdout); +} + +static uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) { + + /* Calculates scale * p / m, but handles m == 0 safely, and saturates */ + + if (p >= m || m == 0) + return scale; + + return scale * p / m; +} + +static void flush_progress(void) { + unsigned n, i; + + if (!on_tty()) + return; + + n = (3 * columns()) / 4; + + putchar('\r'); + + for (i = 0; i < n + 5; i++) + putchar(' '); + + putchar('\r'); + fflush(stdout); +} + +#define debug(_offset, _fmt, ...) do { \ + flush_progress(); \ + log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \ + } while (0) + +#define warning(_offset, _fmt, ...) do { \ + flush_progress(); \ + log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \ + } while (0) + +#define error(_offset, _fmt, ...) do { \ + flush_progress(); \ + log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \ + } while (0) + +static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) { + uint64_t i; + + assert(f); + assert(offset); + assert(o); + + /* This does various superficial tests about the length an + * possible field values. It does not follow any references to + * other objects. */ + + if ((o->object.flags & OBJECT_COMPRESSED_XZ) && + o->object.type != OBJECT_DATA) { + error(offset, "Found compressed object that isn't of type DATA, which is not allowed."); + return -EBADMSG; + } + + switch (o->object.type) { + + case OBJECT_DATA: { + uint64_t h1, h2; + int compression, r; + + if (le64toh(o->data.entry_offset) == 0) + warning(offset, "Unused data (entry_offset==0)"); + + if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) { + error(offset, "Bad n_entries: %"PRIu64, o->data.n_entries); + return -EBADMSG; + } + + if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) { + error(offset, "Bad object size (<= %zu): %"PRIu64, + offsetof(DataObject, payload), + le64toh(o->object.size)); + return -EBADMSG; + } + + h1 = le64toh(o->data.hash); + + compression = o->object.flags & OBJECT_COMPRESSION_MASK; + if (compression) { + _cleanup_free_ void *b = NULL; + size_t alloc = 0, b_size; + + r = decompress_blob(compression, + o->data.payload, + le64toh(o->object.size) - offsetof(Object, data.payload), + &b, &alloc, &b_size, 0); + if (r < 0) { + error(offset, "%s decompression failed: %s", + object_compressed_to_string(compression), strerror(-r)); + return r; + } + + h2 = hash64(b, b_size); + } else + h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); + + if (h1 != h2) { + error(offset, "Invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2); + return -EBADMSG; + } + + if (!VALID64(o->data.next_hash_offset) || + !VALID64(o->data.next_field_offset) || + !VALID64(o->data.entry_offset) || + !VALID64(o->data.entry_array_offset)) { + error(offset, "Invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt, + o->data.next_hash_offset, + o->data.next_field_offset, + o->data.entry_offset, + o->data.entry_array_offset); + return -EBADMSG; + } + + break; + } + + case OBJECT_FIELD: + if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) { + error(offset, + "Bad field size (<= %zu): %"PRIu64, + offsetof(FieldObject, payload), + le64toh(o->object.size)); + return -EBADMSG; + } + + if (!VALID64(o->field.next_hash_offset) || + !VALID64(o->field.head_data_offset)) { + error(offset, + "Invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt, + o->field.next_hash_offset, + o->field.head_data_offset); + return -EBADMSG; + } + break; + + case OBJECT_ENTRY: + if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) { + error(offset, + "Bad entry size (<= %zu): %"PRIu64, + offsetof(EntryObject, items), + le64toh(o->object.size)); + return -EBADMSG; + } + + if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) { + error(offset, + "Invalid number items in entry: %"PRIu64, + (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem)); + return -EBADMSG; + } + + if (le64toh(o->entry.seqnum) <= 0) { + error(offset, + "Invalid entry seqnum: %"PRIx64, + le64toh(o->entry.seqnum)); + return -EBADMSG; + } + + if (!VALID_REALTIME(le64toh(o->entry.realtime))) { + error(offset, + "Invalid entry realtime timestamp: %"PRIu64, + le64toh(o->entry.realtime)); + return -EBADMSG; + } + + if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) { + error(offset, + "Invalid entry monotonic timestamp: %"PRIu64, + le64toh(o->entry.monotonic)); + return -EBADMSG; + } + + for (i = 0; i < journal_file_entry_n_items(o); i++) { + if (o->entry.items[i].object_offset == 0 || + !VALID64(o->entry.items[i].object_offset)) { + error(offset, + "Invalid entry item (%"PRIu64"/%"PRIu64" offset: "OFSfmt, + i, journal_file_entry_n_items(o), + o->entry.items[i].object_offset); + return -EBADMSG; + } + } + + break; + + case OBJECT_DATA_HASH_TABLE: + case OBJECT_FIELD_HASH_TABLE: + if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 || + (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) { + error(offset, + "Invalid %s hash table size: %"PRIu64, + o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", + le64toh(o->object.size)); + return -EBADMSG; + } + + for (i = 0; i < journal_file_hash_table_n_items(o); i++) { + if (o->hash_table.items[i].head_hash_offset != 0 && + !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) { + error(offset, + "Invalid %s hash table item (%"PRIu64"/%"PRIu64") head_hash_offset: "OFSfmt, + o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", + i, journal_file_hash_table_n_items(o), + le64toh(o->hash_table.items[i].head_hash_offset)); + return -EBADMSG; + } + if (o->hash_table.items[i].tail_hash_offset != 0 && + !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset))) { + error(offset, + "Invalid %s hash table item (%"PRIu64"/%"PRIu64") tail_hash_offset: "OFSfmt, + o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", + i, journal_file_hash_table_n_items(o), + le64toh(o->hash_table.items[i].tail_hash_offset)); + return -EBADMSG; + } + + if ((o->hash_table.items[i].head_hash_offset != 0) != + (o->hash_table.items[i].tail_hash_offset != 0)) { + error(offset, + "Invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" tail_hash_offset="OFSfmt, + o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", + i, journal_file_hash_table_n_items(o), + le64toh(o->hash_table.items[i].head_hash_offset), + le64toh(o->hash_table.items[i].tail_hash_offset)); + return -EBADMSG; + } + } + + break; + + case OBJECT_ENTRY_ARRAY: + if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 || + (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) { + error(offset, + "Invalid object entry array size: %"PRIu64, + le64toh(o->object.size)); + return -EBADMSG; + } + + if (!VALID64(o->entry_array.next_entry_array_offset)) { + error(offset, + "Invalid object entry array next_entry_array_offset: "OFSfmt, + o->entry_array.next_entry_array_offset); + return -EBADMSG; + } + + for (i = 0; i < journal_file_entry_array_n_items(o); i++) + if (le64toh(o->entry_array.items[i]) != 0 && + !VALID64(le64toh(o->entry_array.items[i]))) { + error(offset, + "Invalid object entry array item (%"PRIu64"/%"PRIu64"): "OFSfmt, + i, journal_file_entry_array_n_items(o), + le64toh(o->entry_array.items[i])); + return -EBADMSG; + } + + break; + + case OBJECT_TAG: + if (le64toh(o->object.size) != sizeof(TagObject)) { + error(offset, + "Invalid object tag size: %"PRIu64, + le64toh(o->object.size)); + return -EBADMSG; + } + + if (!VALID_EPOCH(o->tag.epoch)) { + error(offset, + "Invalid object tag epoch: %"PRIu64, + o->tag.epoch); + return -EBADMSG; + } + + break; + } + + return 0; +} + +static int write_uint64(int fd, uint64_t p) { + ssize_t k; + + k = write(fd, &p, sizeof(p)); + if (k < 0) + return -errno; + if (k != sizeof(p)) + return -EIO; + + return 0; +} + +static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { + uint64_t a, b; + int r; + + assert(m); + assert(fd >= 0); + + /* Bisection ... */ + + a = 0; b = n; + while (a < b) { + uint64_t c, *z; + + c = (a + b) / 2; + + r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z); + if (r < 0) + return r; + + if (*z == p) + return 1; + + if (a + 1 >= b) + return 0; + + if (p < *z) + b = c; + else + a = c; + } + + return 0; +} + +static int entry_points_to_data( + JournalFile *f, + int entry_fd, + uint64_t n_entries, + uint64_t entry_p, + uint64_t data_p) { + + int r; + uint64_t i, n, a; + Object *o; + bool found = false; + + assert(f); + assert(entry_fd >= 0); + + if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) { + error(data_p, "Data object references invalid entry at "OFSfmt, entry_p); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o); + if (r < 0) + return r; + + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) + if (le64toh(o->entry.items[i].object_offset) == data_p) { + found = true; + break; + } + + if (!found) { + error(entry_p, "Data object at "OFSfmt" not referenced by linked entry", data_p); + return -EBADMSG; + } + + /* Check if this entry is also in main entry array. Since the + * main entry array has already been verified we can rely on + * its consistency. */ + + i = 0; + n = le64toh(f->header->n_entries); + a = le64toh(f->header->entry_array_offset); + + while (i < n) { + uint64_t m, u; + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + m = journal_file_entry_array_n_items(o); + u = MIN(n - i, m); + + if (entry_p <= le64toh(o->entry_array.items[u-1])) { + uint64_t x, y, z; + + x = 0; + y = u; + + while (x < y) { + z = (x + y) / 2; + + if (le64toh(o->entry_array.items[z]) == entry_p) + return 0; + + if (x + 1 >= y) + break; + + if (entry_p < le64toh(o->entry_array.items[z])) + y = z; + else + x = z; + } + + error(entry_p, "Entry object doesn't exist in main entry array"); + return -EBADMSG; + } + + i += u; + a = le64toh(o->entry_array.next_entry_array_offset); + } + + return 0; +} + +static int verify_data( + JournalFile *f, + Object *o, uint64_t p, + int entry_fd, uint64_t n_entries, + int entry_array_fd, uint64_t n_entry_arrays) { + + uint64_t i, n, a, last, q; + int r; + + assert(f); + assert(o); + assert(entry_fd >= 0); + assert(entry_array_fd >= 0); + + n = le64toh(o->data.n_entries); + a = le64toh(o->data.entry_array_offset); + + /* Entry array means at least two objects */ + if (a && n < 2) { + error(p, "Entry array present (entry_array_offset="OFSfmt", but n_entries=%"PRIu64")", a, n); + return -EBADMSG; + } + + if (n == 0) + return 0; + + /* We already checked that earlier */ + assert(o->data.entry_offset); + + last = q = le64toh(o->data.entry_offset); + r = entry_points_to_data(f, entry_fd, n_entries, q, p); + if (r < 0) + return r; + + i = 1; + while (i < n) { + uint64_t next, m, j; + + if (a == 0) { + error(p, "Array chain too short"); + return -EBADMSG; + } + + if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) { + error(p, "Invalid array offset "OFSfmt, a); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + next = le64toh(o->entry_array.next_entry_array_offset); + if (next != 0 && next <= a) { + error(p, "Array chain has cycle (jumps back from "OFSfmt" to "OFSfmt")", a, next); + return -EBADMSG; + } + + m = journal_file_entry_array_n_items(o); + for (j = 0; i < n && j < m; i++, j++) { + + q = le64toh(o->entry_array.items[j]); + if (q <= last) { + error(p, "Data object's entry array not sorted"); + return -EBADMSG; + } + last = q; + + r = entry_points_to_data(f, entry_fd, n_entries, q, p); + if (r < 0) + return r; + + /* Pointer might have moved, reposition */ + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + } + + a = next; + } + + return 0; +} + +static int verify_hash_table( + JournalFile *f, + int data_fd, uint64_t n_data, + int entry_fd, uint64_t n_entries, + int entry_array_fd, uint64_t n_entry_arrays, + usec_t *last_usec, + bool show_progress) { + + uint64_t i, n; + int r; + + assert(f); + assert(data_fd >= 0); + assert(entry_fd >= 0); + assert(entry_array_fd >= 0); + assert(last_usec); + + n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); + if (n <= 0) + return 0; + + r = journal_file_map_data_hash_table(f); + if (r < 0) + return log_error_errno(r, "Failed to map data hash table: %m"); + + for (i = 0; i < n; i++) { + uint64_t last = 0, p; + + if (show_progress) + draw_progress(0xC000 + scale_progress(0x3FFF, i, n), last_usec); + + p = le64toh(f->data_hash_table[i].head_hash_offset); + while (p != 0) { + Object *o; + uint64_t next; + + if (!contains_uint64(f->mmap, data_fd, n_data, p)) { + error(p, "Invalid data object at hash entry %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + next = le64toh(o->data.next_hash_offset); + if (next != 0 && next <= p) { + error(p, "Hash chain has a cycle in hash entry %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + + if (le64toh(o->data.hash) % n != i) { + error(p, "Hash value mismatch in hash entry %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + + r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays); + if (r < 0) + return r; + + last = p; + p = next; + } + + if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) { + error(p, "Tail hash pointer mismatch in hash table"); + return -EBADMSG; + } + } + + return 0; +} + +static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) { + uint64_t n, h, q; + int r; + assert(f); + + n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); + if (n <= 0) + return 0; + + r = journal_file_map_data_hash_table(f); + if (r < 0) + return log_error_errno(r, "Failed to map data hash table: %m"); + + h = hash % n; + + q = le64toh(f->data_hash_table[h].head_hash_offset); + while (q != 0) { + Object *o; + + if (p == q) + return 1; + + r = journal_file_move_to_object(f, OBJECT_DATA, q, &o); + if (r < 0) + return r; + + q = le64toh(o->data.next_hash_offset); + } + + return 0; +} + +static int verify_entry( + JournalFile *f, + Object *o, uint64_t p, + int data_fd, uint64_t n_data) { + + uint64_t i, n; + int r; + + assert(f); + assert(o); + assert(data_fd >= 0); + + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) { + uint64_t q, h; + Object *u; + + q = le64toh(o->entry.items[i].object_offset); + h = le64toh(o->entry.items[i].hash); + + if (!contains_uint64(f->mmap, data_fd, n_data, q)) { + error(p, "Invalid data object of entry"); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_DATA, q, &u); + if (r < 0) + return r; + + if (le64toh(u->data.hash) != h) { + error(p, "Hash mismatch for data object of entry"); + return -EBADMSG; + } + + r = data_object_in_hash_table(f, h, q); + if (r < 0) + return r; + if (r == 0) { + error(p, "Data object missing from hash table"); + return -EBADMSG; + } + } + + return 0; +} + +static int verify_entry_array( + JournalFile *f, + int data_fd, uint64_t n_data, + int entry_fd, uint64_t n_entries, + int entry_array_fd, uint64_t n_entry_arrays, + usec_t *last_usec, + bool show_progress) { + + uint64_t i = 0, a, n, last = 0; + int r; + + assert(f); + assert(data_fd >= 0); + assert(entry_fd >= 0); + assert(entry_array_fd >= 0); + assert(last_usec); + + n = le64toh(f->header->n_entries); + a = le64toh(f->header->entry_array_offset); + while (i < n) { + uint64_t next, m, j; + Object *o; + + if (show_progress) + draw_progress(0x8000 + scale_progress(0x3FFF, i, n), last_usec); + + if (a == 0) { + error(a, "Array chain too short at %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + + if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) { + error(a, "Invalid array %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + next = le64toh(o->entry_array.next_entry_array_offset); + if (next != 0 && next <= a) { + error(a, "Array chain has cycle at %"PRIu64" of %"PRIu64" (jumps back from to "OFSfmt")", i, n, next); + return -EBADMSG; + } + + m = journal_file_entry_array_n_items(o); + for (j = 0; i < n && j < m; i++, j++) { + uint64_t p; + + p = le64toh(o->entry_array.items[j]); + if (p <= last) { + error(a, "Entry array not sorted at %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + last = p; + + if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) { + error(a, "Invalid array entry at %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + r = verify_entry(f, o, p, data_fd, n_data); + if (r < 0) + return r; + + /* Pointer might have moved, reposition */ + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + } + + a = next; + } + + return 0; +} + +int journal_file_verify( + JournalFile *f, + const char *key, + usec_t *first_contained, usec_t *last_validated, usec_t *last_contained, + bool show_progress) { + int r; + Object *o; + uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0; + + uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; + sd_id128_t entry_boot_id; + bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false; + uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0, n_tags = 0; + usec_t last_usec = 0; + int data_fd = -1, entry_fd = -1, entry_array_fd = -1; + unsigned i; + bool found_last = false; +#ifdef HAVE_GCRYPT + uint64_t last_tag = 0; +#endif + assert(f); + + if (key) { +#ifdef HAVE_GCRYPT + r = journal_file_parse_verification_key(f, key); + if (r < 0) { + log_error("Failed to parse seed."); + return r; + } +#else + return -EOPNOTSUPP; +#endif + } else if (f->seal) + return -ENOKEY; + + data_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC); + if (data_fd < 0) { + r = log_error_errno(data_fd, "Failed to create data file: %m"); + goto fail; + } + + entry_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC); + if (entry_fd < 0) { + r = log_error_errno(entry_fd, "Failed to create entry file: %m"); + goto fail; + } + + entry_array_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC); + if (entry_array_fd < 0) { + r = log_error_errno(entry_array_fd, + "Failed to create entry array file: %m"); + goto fail; + } + + if (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SUPPORTED) { + log_error("Cannot verify file with unknown extensions."); + r = -EOPNOTSUPP; + goto fail; + } + + for (i = 0; i < sizeof(f->header->reserved); i++) + if (f->header->reserved[i] != 0) { + error(offsetof(Header, reserved[i]), "Reserved field is non-zero"); + r = -EBADMSG; + goto fail; + } + + /* First iteration: we go through all objects, verify the + * superficial structure, headers, hashes. */ + + p = le64toh(f->header->header_size); + for (;;) { + /* Early exit if there are no objects in the file, at all */ + if (le64toh(f->header->tail_object_offset) == 0) + break; + + if (show_progress) + draw_progress(scale_progress(0x7FFF, p, le64toh(f->header->tail_object_offset)), &last_usec); + + r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); + if (r < 0) { + error(p, "Invalid object"); + goto fail; + } + + if (p > le64toh(f->header->tail_object_offset)) { + error(offsetof(Header, tail_object_offset), "Invalid tail object pointer"); + r = -EBADMSG; + goto fail; + } + + n_objects++; + + r = journal_file_object_verify(f, p, o); + if (r < 0) { + error(p, "Invalid object contents: %s", strerror(-r)); + goto fail; + } + + if ((o->object.flags & OBJECT_COMPRESSED_XZ) && + (o->object.flags & OBJECT_COMPRESSED_LZ4)) { + error(p, "Objected with double compression"); + r = -EINVAL; + goto fail; + } + + if ((o->object.flags & OBJECT_COMPRESSED_XZ) && !JOURNAL_HEADER_COMPRESSED_XZ(f->header)) { + error(p, "XZ compressed object in file without XZ compression"); + r = -EBADMSG; + goto fail; + } + + if ((o->object.flags & OBJECT_COMPRESSED_LZ4) && !JOURNAL_HEADER_COMPRESSED_LZ4(f->header)) { + error(p, "LZ4 compressed object in file without LZ4 compression"); + r = -EBADMSG; + goto fail; + } + + switch (o->object.type) { + + case OBJECT_DATA: + r = write_uint64(data_fd, p); + if (r < 0) + goto fail; + + n_data++; + break; + + case OBJECT_FIELD: + n_fields++; + break; + + case OBJECT_ENTRY: + if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) { + error(p, "First entry before first tag"); + r = -EBADMSG; + goto fail; + } + + r = write_uint64(entry_fd, p); + if (r < 0) + goto fail; + + if (le64toh(o->entry.realtime) < last_tag_realtime) { + error(p, "Older entry after newer tag"); + r = -EBADMSG; + goto fail; + } + + if (!entry_seqnum_set && + le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) { + error(p, "Head entry sequence number incorrect"); + r = -EBADMSG; + goto fail; + } + + if (entry_seqnum_set && + entry_seqnum >= le64toh(o->entry.seqnum)) { + error(p, "Entry sequence number out of synchronization"); + r = -EBADMSG; + goto fail; + } + + entry_seqnum = le64toh(o->entry.seqnum); + entry_seqnum_set = true; + + if (entry_monotonic_set && + sd_id128_equal(entry_boot_id, o->entry.boot_id) && + entry_monotonic > le64toh(o->entry.monotonic)) { + error(p, "Entry timestamp out of synchronization"); + r = -EBADMSG; + goto fail; + } + + entry_monotonic = le64toh(o->entry.monotonic); + entry_boot_id = o->entry.boot_id; + entry_monotonic_set = true; + + if (!entry_realtime_set && + le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) { + error(p, "Head entry realtime timestamp incorrect"); + r = -EBADMSG; + goto fail; + } + + entry_realtime = le64toh(o->entry.realtime); + entry_realtime_set = true; + + n_entries++; + break; + + case OBJECT_DATA_HASH_TABLE: + if (n_data_hash_tables > 1) { + error(p, "More than one data hash table"); + r = -EBADMSG; + goto fail; + } + + if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) || + le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { + error(p, "header fields for data hash table invalid"); + r = -EBADMSG; + goto fail; + } + + n_data_hash_tables++; + break; + + case OBJECT_FIELD_HASH_TABLE: + if (n_field_hash_tables > 1) { + error(p, "More than one field hash table"); + r = -EBADMSG; + goto fail; + } + + if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) || + le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { + error(p, "Header fields for field hash table invalid"); + r = -EBADMSG; + goto fail; + } + + n_field_hash_tables++; + break; + + case OBJECT_ENTRY_ARRAY: + r = write_uint64(entry_array_fd, p); + if (r < 0) + goto fail; + + if (p == le64toh(f->header->entry_array_offset)) { + if (found_main_entry_array) { + error(p, "More than one main entry array"); + r = -EBADMSG; + goto fail; + } + + found_main_entry_array = true; + } + + n_entry_arrays++; + break; + + case OBJECT_TAG: + if (!JOURNAL_HEADER_SEALED(f->header)) { + error(p, "Tag object in file without sealing"); + r = -EBADMSG; + goto fail; + } + + if (le64toh(o->tag.seqnum) != n_tags + 1) { + error(p, "Tag sequence number out of synchronization"); + r = -EBADMSG; + goto fail; + } + + if (le64toh(o->tag.epoch) < last_epoch) { + error(p, "Epoch sequence out of synchronization"); + r = -EBADMSG; + goto fail; + } + +#ifdef HAVE_GCRYPT + if (f->seal) { + uint64_t q, rt; + + debug(p, "Checking tag %"PRIu64"...", le64toh(o->tag.seqnum)); + + rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec; + if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) { + error(p, "tag/entry realtime timestamp out of synchronization"); + r = -EBADMSG; + goto fail; + } + + /* OK, now we know the epoch. So let's now set + * it, and calculate the HMAC for everything + * since the last tag. */ + r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch)); + if (r < 0) + goto fail; + + r = journal_file_hmac_start(f); + if (r < 0) + goto fail; + + if (last_tag == 0) { + r = journal_file_hmac_put_header(f); + if (r < 0) + goto fail; + + q = le64toh(f->header->header_size); + } else + q = last_tag; + + while (q <= p) { + r = journal_file_move_to_object(f, OBJECT_UNUSED, q, &o); + if (r < 0) + goto fail; + + r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q); + if (r < 0) + goto fail; + + q = q + ALIGN64(le64toh(o->object.size)); + } + + /* Position might have changed, let's reposition things */ + r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); + if (r < 0) + goto fail; + + if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) { + error(p, "Tag failed verification"); + r = -EBADMSG; + goto fail; + } + + f->hmac_running = false; + last_tag_realtime = rt; + last_sealed_realtime = entry_realtime; + } + + last_tag = p + ALIGN64(le64toh(o->object.size)); +#endif + + last_epoch = le64toh(o->tag.epoch); + + n_tags++; + break; + + default: + n_weird++; + } + + if (p == le64toh(f->header->tail_object_offset)) { + found_last = true; + break; + } + + p = p + ALIGN64(le64toh(o->object.size)); + }; + + if (!found_last && le64toh(f->header->tail_object_offset) != 0) { + error(le64toh(f->header->tail_object_offset), "Tail object pointer dead"); + r = -EBADMSG; + goto fail; + } + + if (n_objects != le64toh(f->header->n_objects)) { + error(offsetof(Header, n_objects), "Object number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (n_entries != le64toh(f->header->n_entries)) { + error(offsetof(Header, n_entries), "Entry number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_data) && + n_data != le64toh(f->header->n_data)) { + error(offsetof(Header, n_data), "Data number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) && + n_fields != le64toh(f->header->n_fields)) { + error(offsetof(Header, n_fields), "Field number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) && + n_tags != le64toh(f->header->n_tags)) { + error(offsetof(Header, n_tags), "Tag number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) && + n_entry_arrays != le64toh(f->header->n_entry_arrays)) { + error(offsetof(Header, n_entry_arrays), "Entry array number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (!found_main_entry_array && le64toh(f->header->entry_array_offset) != 0) { + error(0, "Missing entry array"); + r = -EBADMSG; + goto fail; + } + + if (entry_seqnum_set && + entry_seqnum != le64toh(f->header->tail_entry_seqnum)) { + error(offsetof(Header, tail_entry_seqnum), "Invalid tail seqnum"); + r = -EBADMSG; + goto fail; + } + + if (entry_monotonic_set && + (!sd_id128_equal(entry_boot_id, f->header->boot_id) || + entry_monotonic != le64toh(f->header->tail_entry_monotonic))) { + error(0, "Invalid tail monotonic timestamp"); + r = -EBADMSG; + goto fail; + } + + if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) { + error(0, "Invalid tail realtime timestamp"); + r = -EBADMSG; + goto fail; + } + + /* Second iteration: we follow all objects referenced from the + * two entry points: the object hash table and the entry + * array. We also check that everything referenced (directly + * or indirectly) in the data hash table also exists in the + * entry array, and vice versa. Note that we do not care for + * unreferenced objects. We only care that everything that is + * referenced is consistent. */ + + r = verify_entry_array(f, + data_fd, n_data, + entry_fd, n_entries, + entry_array_fd, n_entry_arrays, + &last_usec, + show_progress); + if (r < 0) + goto fail; + + r = verify_hash_table(f, + data_fd, n_data, + entry_fd, n_entries, + entry_array_fd, n_entry_arrays, + &last_usec, + show_progress); + if (r < 0) + goto fail; + + if (show_progress) + flush_progress(); + + mmap_cache_close_fd(f->mmap, data_fd); + mmap_cache_close_fd(f->mmap, entry_fd); + mmap_cache_close_fd(f->mmap, entry_array_fd); + + safe_close(data_fd); + safe_close(entry_fd); + safe_close(entry_array_fd); + + if (first_contained) + *first_contained = le64toh(f->header->head_entry_realtime); + if (last_validated) + *last_validated = last_sealed_realtime; + if (last_contained) + *last_contained = le64toh(f->header->tail_entry_realtime); + + return 0; + +fail: + if (show_progress) + flush_progress(); + + log_error("File corruption detected at %s:"OFSfmt" (of %llu bytes, %"PRIu64"%%).", + f->path, + p, + (unsigned long long) f->last_stat.st_size, + 100 * p / f->last_stat.st_size); + + if (data_fd >= 0) { + mmap_cache_close_fd(f->mmap, data_fd); + safe_close(data_fd); + } + + if (entry_fd >= 0) { + mmap_cache_close_fd(f->mmap, entry_fd); + safe_close(entry_fd); + } + + if (entry_array_fd >= 0) { + mmap_cache_close_fd(f->mmap, entry_array_fd); + safe_close(entry_array_fd); + } + + return r; +} diff --git a/src/libsystemd/src/sd-journal/journal-verify.h b/src/libsystemd/src/sd-journal/journal-verify.h new file mode 100644 index 0000000000..8f0eaf6daa --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-verify.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include "journal-file.h" + +int journal_file_verify(JournalFile *f, const char *key, usec_t *first_contained, usec_t *last_validated, usec_t *last_contained, bool show_progress); diff --git a/src/libsystemd/src/sd-journal/lookup3.c b/src/libsystemd/src/sd-journal/lookup3.c new file mode 100644 index 0000000000..3d791234f4 --- /dev/null +++ b/src/libsystemd/src/sd-journal/lookup3.c @@ -0,0 +1,1009 @@ +/* Slightly modified by Lennart Poettering, to avoid name clashes, and + * unexport a few functions. */ + +#include "lookup3.h" + +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ +/* #define SELF_TEST 1 */ + +#include /* defines uint32_t etc */ +#include /* defines printf for tests */ +#include /* attempt to define endianness */ +#include /* defines time_t for timings in the test */ +#ifdef linux +# include /* attempt to define endianness */ +#endif + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +-------------------------------------------------------------------- + This works on all machines. To be useful, it requires + -- that the key be an array of uint32_t's, and + -- that the length be the number of uint32_t's in the key + + The function hashword() is identical to hashlittle() on little-endian + machines, and identical to hashbig() on big-endian machines, + except that the length has to be measured in uint32_ts rather than in + bytes. hashlittle() is more complicated than hashword() only because + hashlittle() has to dance around fitting the key bytes into registers. +-------------------------------------------------------------------- +*/ +uint32_t jenkins_hashword( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t initval) /* the previous hash, or an arbitrary value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + return c; +} + + +/* +-------------------------------------------------------------------- +hashword2() -- same as hashword(), but take two seeds and return two +32-bit values. pc and pb must both be nonnull, and *pc and *pb must +both be initialized with seeds. If you pass in (*pb)==0, the output +(*pc) will be the same as the return value from hashword(). +-------------------------------------------------------------------- +*/ +void jenkins_hashword2 ( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t *pc, /* IN: seed OUT: primary hash value */ +uint32_t *pb) /* IN: more seed OUT: secondary hash value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; + c += *pb; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + *pc=c; *pb=b; +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticeably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + { + const uint8_t *k8 = (const uint8_t *) k; + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +/* + * hashlittle2: return 2 32-bit hash values + * + * This is identical to hashlittle(), except it returns two 32-bit hash + * values instead of just one. This is good enough for hash table + * lookup with 2^^64 buckets, or if you want a second hash if you're not + * happy with the first, or if you want a probably-unique 64-bit ID for + * the key. *pc is better mixed than *pb, so use *pc first. If you want + * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". + */ +void jenkins_hashlittle2( + const void *key, /* the key to hash */ + size_t length, /* length of the key */ + uint32_t *pc, /* IN: primary initval, OUT: primary hash */ + uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */ +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc; + c += *pb; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticeably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + { + const uint8_t *k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + } + + final(a,b,c); + *pc=c; *pb=b; +} + + + +/* + * hashbig(): + * This is the same as hashword() on big-endian machines. It is different + * from hashlittle() on all machines. hashbig() takes advantage of + * big-endian byte ordering. + */ +uint32_t jenkins_hashbig( const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; + union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]<<8" actually reads beyond the end of the string, but + * then shifts out the part it's not allowed to read. Because the + * string is aligned, the illegal read is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticeably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; + case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; + case 5 : b+=k[1]&0xff000000; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff00; break; + case 2 : a+=k[0]&0xffff0000; break; + case 1 : a+=k[0]&0xff000000; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + { + const uint8_t *k8 = (const uint8_t *)k; + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ + case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ + case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ + case 1 : a+=((uint32_t)k8[0])<<24; break; + case 0 : return c; + } + } + +#endif /* !VALGRIND */ + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += ((uint32_t)k[0])<<24; + a += ((uint32_t)k[1])<<16; + a += ((uint32_t)k[2])<<8; + a += ((uint32_t)k[3]); + b += ((uint32_t)k[4])<<24; + b += ((uint32_t)k[5])<<16; + b += ((uint32_t)k[6])<<8; + b += ((uint32_t)k[7]); + c += ((uint32_t)k[8])<<24; + c += ((uint32_t)k[9])<<16; + c += ((uint32_t)k[10])<<8; + c += ((uint32_t)k[11]); + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[11]; + case 11: c+=((uint32_t)k[10])<<8; + case 10: c+=((uint32_t)k[9])<<16; + case 9 : c+=((uint32_t)k[8])<<24; + case 8 : b+=k[7]; + case 7 : b+=((uint32_t)k[6])<<8; + case 6 : b+=((uint32_t)k[5])<<16; + case 5 : b+=((uint32_t)k[4])<<24; + case 4 : a+=k[3]; + case 3 : a+=((uint32_t)k[2])<<8; + case 2 : a+=((uint32_t)k[1])<<16; + case 1 : a+=((uint32_t)k[0])<<24; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +#ifdef SELF_TEST + +/* used for timings */ +void driver1() +{ + uint8_t buf[256]; + uint32_t i; + uint32_t h=0; + time_t a,z; + + time(&a); + for (i=0; i<256; ++i) buf[i] = 'x'; + for (i=0; i<1; ++i) + { + h = hashlittle(&buf[0],1,h); + } + time(&z); + if (z-a > 0) printf("time %d %.8x\n", z-a, h); +} + +/* check that every input bit changes every output bit half the time */ +#define HASHSTATE 1 +#define HASHLEN 1 +#define MAXPAIR 60 +#define MAXLEN 70 +void driver2() +{ + uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1]; + uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z; + uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE]; + uint32_t x[HASHSTATE],y[HASHSTATE]; + uint32_t hlen; + + printf("No more than %d trials should ever be needed \n",MAXPAIR/2); + for (hlen=0; hlen < MAXLEN; ++hlen) + { + z=0; + for (i=0; i>(8-j)); + c[0] = hashlittle(a, hlen, m); + b[i] ^= ((k+1)<>(8-j)); + d[0] = hashlittle(b, hlen, m); + /* check every bit is 1, 0, set, and not set at least once */ + for (l=0; lz) z=k; + if (k==MAXPAIR) + { + printf("Some bit didn't change: "); + printf("%.8x %.8x %.8x %.8x %.8x %.8x ", + e[0],f[0],g[0],h[0],x[0],y[0]); + printf("i %d j %d m %d len %d\n", i, j, m, hlen); + } + if (z==MAXPAIR) goto done; + } + } + } + done: + if (z < MAXPAIR) + { + printf("Mix success %2d bytes %2d initvals ",i,m); + printf("required %d trials\n", z/2); + } + } + printf("\n"); +} + +/* Check for reading beyond the end of the buffer and alignment problems */ +void driver3() +{ + uint8_t buf[MAXLEN+20], *b; + uint32_t len; + uint8_t q[] = "This is the time for all good men to come to the aid of their country..."; + uint32_t h; + uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country..."; + uint32_t i; + uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country..."; + uint32_t j; + uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country..."; + uint32_t ref,x,y; + uint8_t *p; + + printf("Endianness. These lines should all be the same (for values filled in):\n"); + printf("%.8x %.8x %.8x\n", + hashword((const uint32_t *)q, (sizeof(q)-1)/4, 13), + hashword((const uint32_t *)q, (sizeof(q)-5)/4, 13), + hashword((const uint32_t *)q, (sizeof(q)-9)/4, 13)); + p = q; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qq[1]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qqq[2]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qqqq[3]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + printf("\n"); + + /* check that hashlittle2 and hashlittle produce the same results */ + i=47; j=0; + hashlittle2(q, sizeof(q), &i, &j); + if (hashlittle(q, sizeof(q), 47) != i) + printf("hashlittle2 and hashlittle mismatch\n"); + + /* check that hashword2 and hashword produce the same results */ + len = 0xdeadbeef; + i=47, j=0; + hashword2(&len, 1, &i, &j); + if (hashword(&len, 1, 47) != i) + printf("hashword2 and hashword mismatch %x %x\n", + i, hashword(&len, 1, 47)); + + /* check hashlittle doesn't read before or after the ends of the string */ + for (h=0, b=buf+1; h<8; ++h, ++b) + { + for (i=0; i +#include + +#include "macro.h" + +uint32_t jenkins_hashword(const uint32_t *k, size_t length, uint32_t initval) _pure_; +void jenkins_hashword2(const uint32_t *k, size_t length, uint32_t *pc, uint32_t *pb); + +uint32_t jenkins_hashlittle(const void *key, size_t length, uint32_t initval) _pure_; +void jenkins_hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb); + +uint32_t jenkins_hashbig(const void *key, size_t length, uint32_t initval) _pure_; + +static inline uint64_t hash64(const void *data, size_t length) { + uint32_t a = 0, b = 0; + + jenkins_hashlittle2(data, length, &a, &b); + + return ((uint64_t) a << 32ULL) | (uint64_t) b; +} diff --git a/src/libsystemd/src/sd-journal/mmap-cache.c b/src/libsystemd/src/sd-journal/mmap-cache.c new file mode 100644 index 0000000000..6bcd9b6ac8 --- /dev/null +++ b/src/libsystemd/src/sd-journal/mmap-cache.c @@ -0,0 +1,725 @@ +/*** + This file is part of systemd. + + Copyright 2012 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 . +***/ + +#include +#include +#include + +#include "alloc-util.h" +#include "fd-util.h" +#include "hashmap.h" +#include "list.h" +#include "log.h" +#include "macro.h" +#include "mmap-cache.h" +#include "sigbus.h" +#include "util.h" + +typedef struct Window Window; +typedef struct Context Context; +typedef struct FileDescriptor FileDescriptor; + +struct Window { + MMapCache *cache; + + bool invalidated:1; + bool keep_always:1; + bool in_unused:1; + + int prot; + void *ptr; + uint64_t offset; + size_t size; + + FileDescriptor *fd; + + LIST_FIELDS(Window, by_fd); + LIST_FIELDS(Window, unused); + + LIST_HEAD(Context, contexts); +}; + +struct Context { + MMapCache *cache; + unsigned id; + Window *window; + + LIST_FIELDS(Context, by_window); +}; + +struct FileDescriptor { + MMapCache *cache; + int fd; + bool sigbus; + LIST_HEAD(Window, windows); +}; + +struct MMapCache { + int n_ref; + unsigned n_windows; + + unsigned n_hit, n_missed; + + Hashmap *fds; + Context *contexts[MMAP_CACHE_MAX_CONTEXTS]; + + LIST_HEAD(Window, unused); + Window *last_unused; +}; + +#define WINDOWS_MIN 64 + +#ifdef ENABLE_DEBUG_MMAP_CACHE +/* Tiny windows increase mmap activity and the chance of exposing unsafe use. */ +# define WINDOW_SIZE (page_size()) +#else +# define WINDOW_SIZE (8ULL*1024ULL*1024ULL) +#endif + +MMapCache* mmap_cache_new(void) { + MMapCache *m; + + m = new0(MMapCache, 1); + if (!m) + return NULL; + + m->n_ref = 1; + return m; +} + +MMapCache* mmap_cache_ref(MMapCache *m) { + assert(m); + assert(m->n_ref > 0); + + m->n_ref++; + return m; +} + +static void window_unlink(Window *w) { + Context *c; + + assert(w); + + if (w->ptr) + munmap(w->ptr, w->size); + + if (w->fd) + LIST_REMOVE(by_fd, w->fd->windows, w); + + if (w->in_unused) { + if (w->cache->last_unused == w) + w->cache->last_unused = w->unused_prev; + + LIST_REMOVE(unused, w->cache->unused, w); + } + + LIST_FOREACH(by_window, c, w->contexts) { + assert(c->window == w); + c->window = NULL; + } +} + +static void window_invalidate(Window *w) { + assert(w); + + if (w->invalidated) + return; + + /* Replace the window with anonymous pages. This is useful + * when we hit a SIGBUS and want to make sure the file cannot + * trigger any further SIGBUS, possibly overrunning the sigbus + * queue. */ + + assert_se(mmap(w->ptr, w->size, w->prot, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == w->ptr); + w->invalidated = true; +} + +static void window_free(Window *w) { + assert(w); + + window_unlink(w); + w->cache->n_windows--; + free(w); +} + +_pure_ static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) { + assert(w); + assert(fd >= 0); + assert(size > 0); + + return + w->fd && + fd == w->fd->fd && + prot == w->prot && + offset >= w->offset && + offset + size <= w->offset + w->size; +} + +static Window *window_add(MMapCache *m, FileDescriptor *fd, int prot, bool keep_always, uint64_t offset, size_t size, void *ptr) { + Window *w; + + assert(m); + assert(fd); + + if (!m->last_unused || m->n_windows <= WINDOWS_MIN) { + + /* Allocate a new window */ + w = new0(Window, 1); + if (!w) + return NULL; + m->n_windows++; + } else { + + /* Reuse an existing one */ + w = m->last_unused; + window_unlink(w); + zero(*w); + } + + w->cache = m; + w->fd = fd; + w->prot = prot; + w->keep_always = keep_always; + w->offset = offset; + w->size = size; + w->ptr = ptr; + + LIST_PREPEND(by_fd, fd->windows, w); + + return w; +} + +static void context_detach_window(Context *c) { + Window *w; + + assert(c); + + if (!c->window) + return; + + w = c->window; + c->window = NULL; + LIST_REMOVE(by_window, w->contexts, c); + + if (!w->contexts && !w->keep_always) { + /* Not used anymore? */ +#ifdef ENABLE_DEBUG_MMAP_CACHE + /* Unmap unused windows immediately to expose use-after-unmap + * by SIGSEGV. */ + window_free(w); +#else + LIST_PREPEND(unused, c->cache->unused, w); + if (!c->cache->last_unused) + c->cache->last_unused = w; + + w->in_unused = true; +#endif + } +} + +static void context_attach_window(Context *c, Window *w) { + assert(c); + assert(w); + + if (c->window == w) + return; + + context_detach_window(c); + + if (w->in_unused) { + /* Used again? */ + LIST_REMOVE(unused, c->cache->unused, w); + if (c->cache->last_unused == w) + c->cache->last_unused = w->unused_prev; + + w->in_unused = false; + } + + c->window = w; + LIST_PREPEND(by_window, w->contexts, c); +} + +static Context *context_add(MMapCache *m, unsigned id) { + Context *c; + + assert(m); + + c = m->contexts[id]; + if (c) + return c; + + c = new0(Context, 1); + if (!c) + return NULL; + + c->cache = m; + c->id = id; + + assert(!m->contexts[id]); + m->contexts[id] = c; + + return c; +} + +static void context_free(Context *c) { + assert(c); + + context_detach_window(c); + + if (c->cache) { + assert(c->cache->contexts[c->id] == c); + c->cache->contexts[c->id] = NULL; + } + + free(c); +} + +static void fd_free(FileDescriptor *f) { + assert(f); + + while (f->windows) + window_free(f->windows); + + if (f->cache) + assert_se(hashmap_remove(f->cache->fds, FD_TO_PTR(f->fd))); + + free(f); +} + +static FileDescriptor* fd_add(MMapCache *m, int fd) { + FileDescriptor *f; + int r; + + assert(m); + assert(fd >= 0); + + f = hashmap_get(m->fds, FD_TO_PTR(fd)); + if (f) + return f; + + r = hashmap_ensure_allocated(&m->fds, NULL); + if (r < 0) + return NULL; + + f = new0(FileDescriptor, 1); + if (!f) + return NULL; + + f->cache = m; + f->fd = fd; + + r = hashmap_put(m->fds, FD_TO_PTR(fd), f); + if (r < 0) { + free(f); + return NULL; + } + + return f; +} + +static void mmap_cache_free(MMapCache *m) { + FileDescriptor *f; + int i; + + assert(m); + + for (i = 0; i < MMAP_CACHE_MAX_CONTEXTS; i++) + if (m->contexts[i]) + context_free(m->contexts[i]); + + while ((f = hashmap_first(m->fds))) + fd_free(f); + + hashmap_free(m->fds); + + while (m->unused) + window_free(m->unused); + + free(m); +} + +MMapCache* mmap_cache_unref(MMapCache *m) { + + if (!m) + return NULL; + + assert(m->n_ref > 0); + + m->n_ref--; + if (m->n_ref == 0) + mmap_cache_free(m); + + return NULL; +} + +static int make_room(MMapCache *m) { + assert(m); + + if (!m->last_unused) + return 0; + + window_free(m->last_unused); + return 1; +} + +static int try_context( + MMapCache *m, + int fd, + int prot, + unsigned context, + bool keep_always, + uint64_t offset, + size_t size, + void **ret) { + + Context *c; + + assert(m); + assert(m->n_ref > 0); + assert(fd >= 0); + assert(size > 0); + assert(ret); + + c = m->contexts[context]; + if (!c) + return 0; + + assert(c->id == context); + + if (!c->window) + return 0; + + if (!window_matches(c->window, fd, prot, offset, size)) { + + /* Drop the reference to the window, since it's unnecessary now */ + context_detach_window(c); + return 0; + } + + if (c->window->fd->sigbus) + return -EIO; + + c->window->keep_always = c->window->keep_always || keep_always; + + *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset); + return 1; +} + +static int find_mmap( + MMapCache *m, + int fd, + int prot, + unsigned context, + bool keep_always, + uint64_t offset, + size_t size, + void **ret) { + + FileDescriptor *f; + Window *w; + Context *c; + + assert(m); + assert(m->n_ref > 0); + assert(fd >= 0); + assert(size > 0); + + f = hashmap_get(m->fds, FD_TO_PTR(fd)); + if (!f) + return 0; + + assert(f->fd == fd); + + if (f->sigbus) + return -EIO; + + LIST_FOREACH(by_fd, w, f->windows) + if (window_matches(w, fd, prot, offset, size)) + break; + + if (!w) + return 0; + + c = context_add(m, context); + if (!c) + return -ENOMEM; + + context_attach_window(c, w); + w->keep_always = w->keep_always || keep_always; + + *ret = (uint8_t*) w->ptr + (offset - w->offset); + return 1; +} + +static int mmap_try_harder(MMapCache *m, void *addr, int fd, int prot, int flags, uint64_t offset, size_t size, void **res) { + void *ptr; + + assert(m); + assert(fd >= 0); + assert(res); + + for (;;) { + int r; + + ptr = mmap(addr, size, prot, flags, fd, offset); + if (ptr != MAP_FAILED) + break; + if (errno != ENOMEM) + return -errno; + + r = make_room(m); + if (r < 0) + return r; + if (r == 0) + return -ENOMEM; + } + + *res = ptr; + return 0; +} + +static int add_mmap( + MMapCache *m, + int fd, + int prot, + unsigned context, + bool keep_always, + uint64_t offset, + size_t size, + struct stat *st, + void **ret) { + + uint64_t woffset, wsize; + Context *c; + FileDescriptor *f; + Window *w; + void *d; + int r; + + assert(m); + assert(m->n_ref > 0); + assert(fd >= 0); + assert(size > 0); + assert(ret); + + woffset = offset & ~((uint64_t) page_size() - 1ULL); + wsize = size + (offset - woffset); + wsize = PAGE_ALIGN(wsize); + + if (wsize < WINDOW_SIZE) { + uint64_t delta; + + delta = PAGE_ALIGN((WINDOW_SIZE - wsize) / 2); + + if (delta > offset) + woffset = 0; + else + woffset -= delta; + + wsize = WINDOW_SIZE; + } + + if (st) { + /* Memory maps that are larger then the files + underneath have undefined behavior. Hence, clamp + things to the file size if we know it */ + + if (woffset >= (uint64_t) st->st_size) + return -EADDRNOTAVAIL; + + if (woffset + wsize > (uint64_t) st->st_size) + wsize = PAGE_ALIGN(st->st_size - woffset); + } + + r = mmap_try_harder(m, NULL, fd, prot, MAP_SHARED, woffset, wsize, &d); + if (r < 0) + return r; + + c = context_add(m, context); + if (!c) + goto outofmem; + + f = fd_add(m, fd); + if (!f) + goto outofmem; + + w = window_add(m, f, prot, keep_always, woffset, wsize, d); + if (!w) + goto outofmem; + + context_detach_window(c); + c->window = w; + LIST_PREPEND(by_window, w->contexts, c); + + *ret = (uint8_t*) w->ptr + (offset - w->offset); + return 1; + +outofmem: + munmap(d, wsize); + return -ENOMEM; +} + +int mmap_cache_get( + MMapCache *m, + int fd, + int prot, + unsigned context, + bool keep_always, + uint64_t offset, + size_t size, + struct stat *st, + void **ret) { + + int r; + + assert(m); + assert(m->n_ref > 0); + assert(fd >= 0); + assert(size > 0); + assert(ret); + assert(context < MMAP_CACHE_MAX_CONTEXTS); + + /* Check whether the current context is the right one already */ + r = try_context(m, fd, prot, context, keep_always, offset, size, ret); + if (r != 0) { + m->n_hit++; + return r; + } + + /* Search for a matching mmap */ + r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret); + if (r != 0) { + m->n_hit++; + return r; + } + + m->n_missed++; + + /* Create a new mmap */ + return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret); +} + +unsigned mmap_cache_get_hit(MMapCache *m) { + assert(m); + + return m->n_hit; +} + +unsigned mmap_cache_get_missed(MMapCache *m) { + assert(m); + + return m->n_missed; +} + +static void mmap_cache_process_sigbus(MMapCache *m) { + bool found = false; + FileDescriptor *f; + Iterator i; + int r; + + assert(m); + + /* Iterate through all triggered pages and mark their files as + * invalidated */ + for (;;) { + bool ours; + void *addr; + + r = sigbus_pop(&addr); + if (_likely_(r == 0)) + break; + if (r < 0) { + log_error_errno(r, "SIGBUS handling failed: %m"); + abort(); + } + + ours = false; + HASHMAP_FOREACH(f, m->fds, i) { + Window *w; + + LIST_FOREACH(by_fd, w, f->windows) { + if ((uint8_t*) addr >= (uint8_t*) w->ptr && + (uint8_t*) addr < (uint8_t*) w->ptr + w->size) { + found = ours = f->sigbus = true; + break; + } + } + + if (ours) + break; + } + + /* Didn't find a matching window, give up */ + if (!ours) { + log_error("Unknown SIGBUS page, aborting."); + abort(); + } + } + + /* The list of triggered pages is now empty. Now, let's remap + * all windows of the triggered file to anonymous maps, so + * that no page of the file in question is triggered again, so + * that we can be sure not to hit the queue size limit. */ + if (_likely_(!found)) + return; + + HASHMAP_FOREACH(f, m->fds, i) { + Window *w; + + if (!f->sigbus) + continue; + + LIST_FOREACH(by_fd, w, f->windows) + window_invalidate(w); + } +} + +bool mmap_cache_got_sigbus(MMapCache *m, int fd) { + FileDescriptor *f; + + assert(m); + assert(fd >= 0); + + mmap_cache_process_sigbus(m); + + f = hashmap_get(m->fds, FD_TO_PTR(fd)); + if (!f) + return false; + + return f->sigbus; +} + +void mmap_cache_close_fd(MMapCache *m, int fd) { + FileDescriptor *f; + + assert(m); + assert(fd >= 0); + + /* Make sure that any queued SIGBUS are first dispatched, so + * that we don't end up with a SIGBUS entry we cannot relate + * to any existing memory map */ + + mmap_cache_process_sigbus(m); + + f = hashmap_get(m->fds, FD_TO_PTR(fd)); + if (!f) + return; + + fd_free(f); +} diff --git a/src/libsystemd/src/sd-journal/mmap-cache.h b/src/libsystemd/src/sd-journal/mmap-cache.h new file mode 100644 index 0000000000..199d944647 --- /dev/null +++ b/src/libsystemd/src/sd-journal/mmap-cache.h @@ -0,0 +1,49 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 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 . +***/ + +#include +#include + +/* One context per object type, plus one of the header, plus one "additional" one */ +#define MMAP_CACHE_MAX_CONTEXTS 9 + +typedef struct MMapCache MMapCache; + +MMapCache* mmap_cache_new(void); +MMapCache* mmap_cache_ref(MMapCache *m); +MMapCache* mmap_cache_unref(MMapCache *m); + +int mmap_cache_get( + MMapCache *m, + int fd, + int prot, + unsigned context, + bool keep_always, + uint64_t offset, + size_t size, + struct stat *st, + void **ret); +void mmap_cache_close_fd(MMapCache *m, int fd); + +unsigned mmap_cache_get_hit(MMapCache *m); +unsigned mmap_cache_get_missed(MMapCache *m); + +bool mmap_cache_got_sigbus(MMapCache *m, int fd); diff --git a/src/libsystemd/src/sd-journal/sd-journal.c b/src/libsystemd/src/sd-journal/sd-journal.c new file mode 100644 index 0000000000..930486d65f --- /dev/null +++ b/src/libsystemd/src/sd-journal/sd-journal.c @@ -0,0 +1,2985 @@ +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "catalog.h" +#include "compress.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "formats-util.h" +#include "fs-util.h" +#include "hashmap.h" +#include "hostname-util.h" +#include "io-util.h" +#include "journal-def.h" +#include "journal-file.h" +#include "journal-internal.h" +#include "list.h" +#include "lookup3.h" +#include "missing.h" +#include "path-util.h" +#include "replace-var.h" +#include "stat-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "strv.h" + +#define JOURNAL_FILES_MAX 7168 + +#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC) + +#define REPLACE_VAR_MAX 256 + +#define DEFAULT_DATA_THRESHOLD (64*1024) + +static void remove_file_real(sd_journal *j, JournalFile *f); + +static bool journal_pid_changed(sd_journal *j) { + assert(j); + + /* We don't support people creating a journal object and + * keeping it around over a fork(). Let's complain. */ + + return j->original_pid != getpid(); +} + +static int journal_put_error(sd_journal *j, int r, const char *path) { + char *copy; + int k; + + /* Memorize an error we encountered, and store which + * file/directory it was generated from. Note that we store + * only *one* path per error code, as the error code is the + * key into the hashmap, and the path is the value. This means + * we keep track only of all error kinds, but not of all error + * locations. This has the benefit that the hashmap cannot + * grow beyond bounds. + * + * We return an error here only if we didn't manage to + * memorize the real error. */ + + if (r >= 0) + return r; + + k = hashmap_ensure_allocated(&j->errors, NULL); + if (k < 0) + return k; + + if (path) { + copy = strdup(path); + if (!copy) + return -ENOMEM; + } else + copy = NULL; + + k = hashmap_put(j->errors, INT_TO_PTR(r), copy); + if (k < 0) { + free(copy); + + if (k == -EEXIST) + return 0; + + return k; + } + + return 0; +} + +static void detach_location(sd_journal *j) { + Iterator i; + JournalFile *f; + + assert(j); + + j->current_file = NULL; + j->current_field = 0; + + ORDERED_HASHMAP_FOREACH(f, j->files, i) + journal_file_reset_location(f); +} + +static void reset_location(sd_journal *j) { + assert(j); + + detach_location(j); + zero(j->current_location); +} + +static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) { + assert(l); + assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK); + assert(f); + assert(o->object.type == OBJECT_ENTRY); + + l->type = type; + l->seqnum = le64toh(o->entry.seqnum); + l->seqnum_id = f->header->seqnum_id; + l->realtime = le64toh(o->entry.realtime); + l->monotonic = le64toh(o->entry.monotonic); + l->boot_id = o->entry.boot_id; + l->xor_hash = le64toh(o->entry.xor_hash); + + l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true; +} + +static void set_location(sd_journal *j, JournalFile *f, Object *o) { + assert(j); + assert(f); + assert(o); + + init_location(&j->current_location, LOCATION_DISCRETE, f, o); + + j->current_file = f; + j->current_field = 0; + + /* Let f know its candidate entry was picked. */ + assert(f->location_type == LOCATION_SEEK); + f->location_type = LOCATION_DISCRETE; +} + +static int match_is_valid(const void *data, size_t size) { + const char *b, *p; + + assert(data); + + if (size < 2) + return false; + + if (startswith(data, "__")) + return false; + + b = data; + for (p = b; p < b + size; p++) { + + if (*p == '=') + return p > b; + + if (*p == '_') + continue; + + if (*p >= 'A' && *p <= 'Z') + continue; + + if (*p >= '0' && *p <= '9') + continue; + + return false; + } + + return false; +} + +static bool same_field(const void *_a, size_t s, const void *_b, size_t t) { + const uint8_t *a = _a, *b = _b; + size_t j; + + for (j = 0; j < s && j < t; j++) { + + if (a[j] != b[j]) + return false; + + if (a[j] == '=') + return true; + } + + assert_not_reached("\"=\" not found"); +} + +static Match *match_new(Match *p, MatchType t) { + Match *m; + + m = new0(Match, 1); + if (!m) + return NULL; + + m->type = t; + + if (p) { + m->parent = p; + LIST_PREPEND(matches, p->matches, m); + } + + return m; +} + +static void match_free(Match *m) { + assert(m); + + while (m->matches) + match_free(m->matches); + + if (m->parent) + LIST_REMOVE(matches, m->parent->matches, m); + + free(m->data); + free(m); +} + +static void match_free_if_empty(Match *m) { + if (!m || m->matches) + return; + + match_free(m); +} + +_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { + Match *l3, *l4, *add_here = NULL, *m; + le64_t le_hash; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(data, -EINVAL); + + if (size == 0) + size = strlen(data); + + assert_return(match_is_valid(data, size), -EINVAL); + + /* level 0: AND term + * level 1: OR terms + * level 2: AND terms + * level 3: OR terms + * level 4: concrete matches */ + + if (!j->level0) { + j->level0 = match_new(NULL, MATCH_AND_TERM); + if (!j->level0) + return -ENOMEM; + } + + if (!j->level1) { + j->level1 = match_new(j->level0, MATCH_OR_TERM); + if (!j->level1) + return -ENOMEM; + } + + if (!j->level2) { + j->level2 = match_new(j->level1, MATCH_AND_TERM); + if (!j->level2) + return -ENOMEM; + } + + assert(j->level0->type == MATCH_AND_TERM); + assert(j->level1->type == MATCH_OR_TERM); + assert(j->level2->type == MATCH_AND_TERM); + + le_hash = htole64(hash64(data, size)); + + LIST_FOREACH(matches, l3, j->level2->matches) { + assert(l3->type == MATCH_OR_TERM); + + LIST_FOREACH(matches, l4, l3->matches) { + assert(l4->type == MATCH_DISCRETE); + + /* Exactly the same match already? Then ignore + * this addition */ + if (l4->le_hash == le_hash && + l4->size == size && + memcmp(l4->data, data, size) == 0) + return 0; + + /* Same field? Then let's add this to this OR term */ + if (same_field(data, size, l4->data, l4->size)) { + add_here = l3; + break; + } + } + + if (add_here) + break; + } + + if (!add_here) { + add_here = match_new(j->level2, MATCH_OR_TERM); + if (!add_here) + goto fail; + } + + m = match_new(add_here, MATCH_DISCRETE); + if (!m) + goto fail; + + m->le_hash = le_hash; + m->size = size; + m->data = memdup(data, size); + if (!m->data) + goto fail; + + detach_location(j); + + return 0; + +fail: + match_free_if_empty(add_here); + match_free_if_empty(j->level2); + match_free_if_empty(j->level1); + match_free_if_empty(j->level0); + + return -ENOMEM; +} + +_public_ int sd_journal_add_conjunction(sd_journal *j) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + if (!j->level0) + return 0; + + if (!j->level1) + return 0; + + if (!j->level1->matches) + return 0; + + j->level1 = NULL; + j->level2 = NULL; + + return 0; +} + +_public_ int sd_journal_add_disjunction(sd_journal *j) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + if (!j->level0) + return 0; + + if (!j->level1) + return 0; + + if (!j->level2) + return 0; + + if (!j->level2->matches) + return 0; + + j->level2 = NULL; + return 0; +} + +static char *match_make_string(Match *m) { + char *p, *r; + Match *i; + bool enclose = false; + + if (!m) + return strdup("none"); + + if (m->type == MATCH_DISCRETE) + return strndup(m->data, m->size); + + p = NULL; + LIST_FOREACH(matches, i, m->matches) { + char *t, *k; + + t = match_make_string(i); + if (!t) { + free(p); + return NULL; + } + + if (p) { + k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL); + free(p); + free(t); + + if (!k) + return NULL; + + p = k; + + enclose = true; + } else + p = t; + } + + if (enclose) { + r = strjoin("(", p, ")", NULL); + free(p); + return r; + } + + return p; +} + +char *journal_make_match_string(sd_journal *j) { + assert(j); + + return match_make_string(j->level0); +} + +_public_ void sd_journal_flush_matches(sd_journal *j) { + if (!j) + return; + + if (j->level0) + match_free(j->level0); + + j->level0 = j->level1 = j->level2 = NULL; + + detach_location(j); +} + +_pure_ static int compare_with_location(JournalFile *f, Location *l) { + assert(f); + assert(l); + assert(f->location_type == LOCATION_SEEK); + assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK); + + if (l->monotonic_set && + sd_id128_equal(f->current_boot_id, l->boot_id) && + l->realtime_set && + f->current_realtime == l->realtime && + l->xor_hash_set && + f->current_xor_hash == l->xor_hash) + return 0; + + if (l->seqnum_set && + sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) { + + if (f->current_seqnum < l->seqnum) + return -1; + if (f->current_seqnum > l->seqnum) + return 1; + } + + if (l->monotonic_set && + sd_id128_equal(f->current_boot_id, l->boot_id)) { + + if (f->current_monotonic < l->monotonic) + return -1; + if (f->current_monotonic > l->monotonic) + return 1; + } + + if (l->realtime_set) { + + if (f->current_realtime < l->realtime) + return -1; + if (f->current_realtime > l->realtime) + return 1; + } + + if (l->xor_hash_set) { + + if (f->current_xor_hash < l->xor_hash) + return -1; + if (f->current_xor_hash > l->xor_hash) + return 1; + } + + return 0; +} + +static int next_for_match( + sd_journal *j, + Match *m, + JournalFile *f, + uint64_t after_offset, + direction_t direction, + Object **ret, + uint64_t *offset) { + + int r; + uint64_t np = 0; + Object *n; + + assert(j); + assert(m); + assert(f); + + if (m->type == MATCH_DISCRETE) { + uint64_t dp; + + r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp); + if (r <= 0) + return r; + + return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset); + + } else if (m->type == MATCH_OR_TERM) { + Match *i; + + /* Find the earliest match beyond after_offset */ + + LIST_FOREACH(matches, i, m->matches) { + uint64_t cp; + + r = next_for_match(j, i, f, after_offset, direction, NULL, &cp); + if (r < 0) + return r; + else if (r > 0) { + if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np)) + np = cp; + } + } + + if (np == 0) + return 0; + + } else if (m->type == MATCH_AND_TERM) { + Match *i, *last_moved; + + /* Always jump to the next matching entry and repeat + * this until we find an offset that matches for all + * matches. */ + + if (!m->matches) + return 0; + + r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np); + if (r <= 0) + return r; + + assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset); + last_moved = m->matches; + + LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) { + uint64_t cp; + + r = next_for_match(j, i, f, np, direction, NULL, &cp); + if (r <= 0) + return r; + + assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np); + if (direction == DIRECTION_DOWN ? cp > np : cp < np) { + np = cp; + last_moved = i; + } + } + } + + assert(np > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n); + if (r < 0) + return r; + + if (ret) + *ret = n; + if (offset) + *offset = np; + + return 1; +} + +static int find_location_for_match( + sd_journal *j, + Match *m, + JournalFile *f, + direction_t direction, + Object **ret, + uint64_t *offset) { + + int r; + + assert(j); + assert(m); + assert(f); + + if (m->type == MATCH_DISCRETE) { + uint64_t dp; + + r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp); + if (r <= 0) + return r; + + /* FIXME: missing: find by monotonic */ + + if (j->current_location.type == LOCATION_HEAD) + return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset); + if (j->current_location.type == LOCATION_TAIL) + return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset); + if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) + return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset); + if (j->current_location.monotonic_set) { + r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset); + if (r != -ENOENT) + return r; + } + if (j->current_location.realtime_set) + return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset); + + return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset); + + } else if (m->type == MATCH_OR_TERM) { + uint64_t np = 0; + Object *n; + Match *i; + + /* Find the earliest match */ + + LIST_FOREACH(matches, i, m->matches) { + uint64_t cp; + + r = find_location_for_match(j, i, f, direction, NULL, &cp); + if (r < 0) + return r; + else if (r > 0) { + if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp)) + np = cp; + } + } + + if (np == 0) + return 0; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n); + if (r < 0) + return r; + + if (ret) + *ret = n; + if (offset) + *offset = np; + + return 1; + + } else { + Match *i; + uint64_t np = 0; + + assert(m->type == MATCH_AND_TERM); + + /* First jump to the last match, and then find the + * next one where all matches match */ + + if (!m->matches) + return 0; + + LIST_FOREACH(matches, i, m->matches) { + uint64_t cp; + + r = find_location_for_match(j, i, f, direction, NULL, &cp); + if (r <= 0) + return r; + + if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np)) + np = cp; + } + + return next_for_match(j, m, f, np, direction, ret, offset); + } +} + +static int find_location_with_matches( + sd_journal *j, + JournalFile *f, + direction_t direction, + Object **ret, + uint64_t *offset) { + + int r; + + assert(j); + assert(f); + assert(ret); + assert(offset); + + if (!j->level0) { + /* No matches is simple */ + + if (j->current_location.type == LOCATION_HEAD) + return journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset); + if (j->current_location.type == LOCATION_TAIL) + return journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset); + if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) + return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset); + if (j->current_location.monotonic_set) { + r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset); + if (r != -ENOENT) + return r; + } + if (j->current_location.realtime_set) + return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset); + + return journal_file_next_entry(f, 0, direction, ret, offset); + } else + return find_location_for_match(j, j->level0, f, direction, ret, offset); +} + +static int next_with_matches( + sd_journal *j, + JournalFile *f, + direction_t direction, + Object **ret, + uint64_t *offset) { + + assert(j); + assert(f); + assert(ret); + assert(offset); + + /* No matches is easy. We simple advance the file + * pointer by one. */ + if (!j->level0) + return journal_file_next_entry(f, f->current_offset, direction, ret, offset); + + /* If we have a match then we look for the next matching entry + * with an offset at least one step larger */ + return next_for_match(j, j->level0, f, + direction == DIRECTION_DOWN ? f->current_offset + 1 + : f->current_offset - 1, + direction, ret, offset); +} + +static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) { + Object *c; + uint64_t cp, n_entries; + int r; + + assert(j); + assert(f); + + n_entries = le64toh(f->header->n_entries); + + /* If we hit EOF before, we don't need to look into this file again + * unless direction changed or new entries appeared. */ + if (f->last_direction == direction && f->location_type == LOCATION_TAIL && + n_entries == f->last_n_entries) + return 0; + + f->last_n_entries = n_entries; + + if (f->last_direction == direction && f->current_offset > 0) { + /* LOCATION_SEEK here means we did the work in a previous + * iteration and the current location already points to a + * candidate entry. */ + if (f->location_type != LOCATION_SEEK) { + r = next_with_matches(j, f, direction, &c, &cp); + if (r <= 0) + return r; + + journal_file_save_location(f, c, cp); + } + } else { + f->last_direction = direction; + + r = find_location_with_matches(j, f, direction, &c, &cp); + if (r <= 0) + return r; + + journal_file_save_location(f, c, cp); + } + + /* OK, we found the spot, now let's advance until an entry + * that is actually different from what we were previously + * looking at. This is necessary to handle entries which exist + * in two (or more) journal files, and which shall all be + * suppressed but one. */ + + for (;;) { + bool found; + + if (j->current_location.type == LOCATION_DISCRETE) { + int k; + + k = compare_with_location(f, &j->current_location); + + found = direction == DIRECTION_DOWN ? k > 0 : k < 0; + } else + found = true; + + if (found) + return 1; + + r = next_with_matches(j, f, direction, &c, &cp); + if (r <= 0) + return r; + + journal_file_save_location(f, c, cp); + } +} + +static int real_journal_next(sd_journal *j, direction_t direction) { + JournalFile *f, *new_file = NULL; + Iterator i; + Object *o; + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + bool found; + + r = next_beyond_location(j, f, direction); + if (r < 0) { + log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path); + remove_file_real(j, f); + continue; + } else if (r == 0) { + f->location_type = LOCATION_TAIL; + continue; + } + + if (!new_file) + found = true; + else { + int k; + + k = journal_file_compare_locations(f, new_file); + + found = direction == DIRECTION_DOWN ? k < 0 : k > 0; + } + + if (found) + new_file = f; + } + + if (!new_file) + return 0; + + r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o); + if (r < 0) + return r; + + set_location(j, new_file, o); + + return 1; +} + +_public_ int sd_journal_next(sd_journal *j) { + return real_journal_next(j, DIRECTION_DOWN); +} + +_public_ int sd_journal_previous(sd_journal *j) { + return real_journal_next(j, DIRECTION_UP); +} + +static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) { + int c = 0, r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + if (skip == 0) { + /* If this is not a discrete skip, then at least + * resolve the current location */ + if (j->current_location.type != LOCATION_DISCRETE) + return real_journal_next(j, direction); + + return 0; + } + + do { + r = real_journal_next(j, direction); + if (r < 0) + return r; + + if (r == 0) + return c; + + skip--; + c++; + } while (skip > 0); + + return c; +} + +_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) { + return real_journal_next_skip(j, DIRECTION_DOWN, skip); +} + +_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) { + return real_journal_next_skip(j, DIRECTION_UP, skip); +} + +_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) { + Object *o; + int r; + char bid[33], sid[33]; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(cursor, -EINVAL); + + if (!j->current_file || j->current_file->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o); + if (r < 0) + return r; + + sd_id128_to_string(j->current_file->header->seqnum_id, sid); + sd_id128_to_string(o->entry.boot_id, bid); + + if (asprintf(cursor, + "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64, + sid, le64toh(o->entry.seqnum), + bid, le64toh(o->entry.monotonic), + le64toh(o->entry.realtime), + le64toh(o->entry.xor_hash)) < 0) + return -ENOMEM; + + return 0; +} + +_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { + const char *word, *state; + size_t l; + unsigned long long seqnum, monotonic, realtime, xor_hash; + bool + seqnum_id_set = false, + seqnum_set = false, + boot_id_set = false, + monotonic_set = false, + realtime_set = false, + xor_hash_set = false; + sd_id128_t seqnum_id, boot_id; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(!isempty(cursor), -EINVAL); + + FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) { + char *item; + int k = 0; + + if (l < 2 || word[1] != '=') + return -EINVAL; + + item = strndup(word, l); + if (!item) + return -ENOMEM; + + switch (word[0]) { + + case 's': + seqnum_id_set = true; + k = sd_id128_from_string(item+2, &seqnum_id); + break; + + case 'i': + seqnum_set = true; + if (sscanf(item+2, "%llx", &seqnum) != 1) + k = -EINVAL; + break; + + case 'b': + boot_id_set = true; + k = sd_id128_from_string(item+2, &boot_id); + break; + + case 'm': + monotonic_set = true; + if (sscanf(item+2, "%llx", &monotonic) != 1) + k = -EINVAL; + break; + + case 't': + realtime_set = true; + if (sscanf(item+2, "%llx", &realtime) != 1) + k = -EINVAL; + break; + + case 'x': + xor_hash_set = true; + if (sscanf(item+2, "%llx", &xor_hash) != 1) + k = -EINVAL; + break; + } + + free(item); + + if (k < 0) + return k; + } + + if ((!seqnum_set || !seqnum_id_set) && + (!monotonic_set || !boot_id_set) && + !realtime_set) + return -EINVAL; + + reset_location(j); + + j->current_location.type = LOCATION_SEEK; + + if (realtime_set) { + j->current_location.realtime = (uint64_t) realtime; + j->current_location.realtime_set = true; + } + + if (seqnum_set && seqnum_id_set) { + j->current_location.seqnum = (uint64_t) seqnum; + j->current_location.seqnum_id = seqnum_id; + j->current_location.seqnum_set = true; + } + + if (monotonic_set && boot_id_set) { + j->current_location.monotonic = (uint64_t) monotonic; + j->current_location.boot_id = boot_id; + j->current_location.monotonic_set = true; + } + + if (xor_hash_set) { + j->current_location.xor_hash = (uint64_t) xor_hash; + j->current_location.xor_hash_set = true; + } + + return 0; +} + +_public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) { + int r; + Object *o; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(!isempty(cursor), -EINVAL); + + if (!j->current_file || j->current_file->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o); + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ char *item = NULL; + unsigned long long ll; + sd_id128_t id; + int k = 0; + + r = extract_first_word(&cursor, &item, ";", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + + if (r == 0) + break; + + if (strlen(item) < 2 || item[1] != '=') + return -EINVAL; + + switch (item[0]) { + + case 's': + k = sd_id128_from_string(item+2, &id); + if (k < 0) + return k; + if (!sd_id128_equal(id, j->current_file->header->seqnum_id)) + return 0; + break; + + case 'i': + if (sscanf(item+2, "%llx", &ll) != 1) + return -EINVAL; + if (ll != le64toh(o->entry.seqnum)) + return 0; + break; + + case 'b': + k = sd_id128_from_string(item+2, &id); + if (k < 0) + return k; + if (!sd_id128_equal(id, o->entry.boot_id)) + return 0; + break; + + case 'm': + if (sscanf(item+2, "%llx", &ll) != 1) + return -EINVAL; + if (ll != le64toh(o->entry.monotonic)) + return 0; + break; + + case 't': + if (sscanf(item+2, "%llx", &ll) != 1) + return -EINVAL; + if (ll != le64toh(o->entry.realtime)) + return 0; + break; + + case 'x': + if (sscanf(item+2, "%llx", &ll) != 1) + return -EINVAL; + if (ll != le64toh(o->entry.xor_hash)) + return 0; + break; + } + } + + return 1; +} + + +_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + reset_location(j); + j->current_location.type = LOCATION_SEEK; + j->current_location.boot_id = boot_id; + j->current_location.monotonic = usec; + j->current_location.monotonic_set = true; + + return 0; +} + +_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + reset_location(j); + j->current_location.type = LOCATION_SEEK; + j->current_location.realtime = usec; + j->current_location.realtime_set = true; + + return 0; +} + +_public_ int sd_journal_seek_head(sd_journal *j) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + reset_location(j); + j->current_location.type = LOCATION_HEAD; + + return 0; +} + +_public_ int sd_journal_seek_tail(sd_journal *j) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + reset_location(j); + j->current_location.type = LOCATION_TAIL; + + return 0; +} + +static void check_network(sd_journal *j, int fd) { + struct statfs sfs; + + assert(j); + + if (j->on_network) + return; + + if (fstatfs(fd, &sfs) < 0) + return; + + j->on_network = + F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) || + F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC); +} + +static bool file_has_type_prefix(const char *prefix, const char *filename) { + const char *full, *tilded, *atted; + + full = strjoina(prefix, ".journal"); + tilded = strjoina(full, "~"); + atted = strjoina(prefix, "@"); + + return streq(filename, full) || + streq(filename, tilded) || + startswith(filename, atted); +} + +static bool file_type_wanted(int flags, const char *filename) { + assert(filename); + + if (!endswith(filename, ".journal") && !endswith(filename, ".journal~")) + return false; + + /* no flags set → every type is OK */ + if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER))) + return true; + + if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename)) + return true; + + if (flags & SD_JOURNAL_CURRENT_USER) { + char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1]; + + xsprintf(prefix, "user-"UID_FMT, getuid()); + + if (file_has_type_prefix(prefix, filename)) + return true; + } + + return false; +} + +static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix) { + assert(j); + assert(path); + assert(prefix); + + if (j->toplevel_fd >= 0) + return false; + + return path_startswith(path, prefix); +} + +static const char *skip_slash(const char *p) { + + if (!p) + return NULL; + + while (*p == '/') + p++; + + return p; +} + +static int add_any_file(sd_journal *j, int fd, const char *path) { + JournalFile *f = NULL; + bool close_fd = false; + int r, k; + + assert(j); + assert(fd >= 0 || path); + + if (path && ordered_hashmap_get(j->files, path)) + return 0; + + if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) { + log_debug("Too many open journal files, not adding %s.", path); + r = -ETOOMANYREFS; + goto fail; + } + + if (fd < 0 && j->toplevel_fd >= 0) { + + /* If there's a top-level fd defined, open the file relative to this now. (Make the path relative, + * explicitly, since otherwise openat() ignores the first argument.) */ + + fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC); + if (fd < 0) { + r = log_debug_errno(errno, "Failed to open journal file %s: %m", path); + goto fail; + } + + close_fd = true; + } + + r = journal_file_open(fd, path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f); + if (r < 0) { + if (close_fd) + safe_close(fd); + log_debug_errno(r, "Failed to open journal file %s: %m", path); + goto fail; + } + + /* journal_file_dump(f); */ + + r = ordered_hashmap_put(j->files, f->path, f); + if (r < 0) { + f->close_fd = close_fd; + (void) journal_file_close(f); + goto fail; + } + + if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run")) + j->has_runtime_files = true; + else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var")) + j->has_persistent_files = true; + + log_debug("File %s added.", f->path); + + check_network(j, f->fd); + + j->current_invalidate_counter++; + + return 0; + +fail: + k = journal_put_error(j, r, path); + if (k < 0) + return k; + + return r; +} + +static int add_file(sd_journal *j, const char *prefix, const char *filename) { + const char *path; + + assert(j); + assert(prefix); + assert(filename); + + if (j->no_new_files) + return 0; + + if (!file_type_wanted(j->flags, filename)) + return 0; + + path = strjoina(prefix, "/", filename); + return add_any_file(j, -1, path); +} + +static void remove_file(sd_journal *j, const char *prefix, const char *filename) { + const char *path; + JournalFile *f; + + assert(j); + assert(prefix); + assert(filename); + + path = strjoina(prefix, "/", filename); + f = ordered_hashmap_get(j->files, path); + if (!f) + return; + + remove_file_real(j, f); +} + +static void remove_file_real(sd_journal *j, JournalFile *f) { + assert(j); + assert(f); + + ordered_hashmap_remove(j->files, f->path); + + log_debug("File %s removed.", f->path); + + if (j->current_file == f) { + j->current_file = NULL; + j->current_field = 0; + } + + if (j->unique_file == f) { + /* Jump to the next unique_file or NULL if that one was last */ + j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path); + j->unique_offset = 0; + if (!j->unique_file) + j->unique_file_lost = true; + } + + if (j->fields_file == f) { + j->fields_file = ordered_hashmap_next(j->files, j->fields_file->path); + j->fields_offset = 0; + if (!j->fields_file) + j->fields_file_lost = true; + } + + (void) journal_file_close(f); + + j->current_invalidate_counter++; +} + +static int dirname_is_machine_id(const char *fn) { + sd_id128_t id, machine; + int r; + + r = sd_id128_get_machine(&machine); + if (r < 0) + return r; + + r = sd_id128_from_string(fn, &id); + if (r < 0) + return r; + + return sd_id128_equal(id, machine); +} + +static int add_directory(sd_journal *j, const char *prefix, const char *dirname) { + _cleanup_free_ char *path = NULL; + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de = NULL; + Directory *m; + int r, k; + + assert(j); + assert(prefix); + + /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch + * and reenumerates directory contents */ + + if (dirname) + path = strjoin(prefix, "/", dirname, NULL); + else + path = strdup(prefix); + if (!path) { + r = -ENOMEM; + goto fail; + } + + log_debug("Considering directory %s.", path); + + /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */ + if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && + !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run"))) + return 0; + + + if (j->toplevel_fd < 0) + d = opendir(path); + else + /* Open the specified directory relative to the the toplevel fd. Enforce that the path specified is + * relative, by dropping the initial slash */ + d = xopendirat(j->toplevel_fd, skip_slash(path), 0); + if (!d) { + r = log_debug_errno(errno, "Failed to open directory %s: %m", path); + goto fail; + } + + m = hashmap_get(j->directories_by_path, path); + if (!m) { + m = new0(Directory, 1); + if (!m) { + r = -ENOMEM; + goto fail; + } + + m->is_root = false; + m->path = path; + + if (hashmap_put(j->directories_by_path, m->path, m) < 0) { + free(m); + r = -ENOMEM; + goto fail; + } + + path = NULL; /* avoid freeing in cleanup */ + j->current_invalidate_counter++; + + log_debug("Directory %s added.", m->path); + + } else if (m->is_root) + return 0; + + if (m->wd <= 0 && j->inotify_fd >= 0) { + /* Watch this directory, if it not being watched yet. */ + + m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d), + IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| + IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM| + IN_ONLYDIR); + + if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0) + inotify_rm_watch(j->inotify_fd, m->wd); + } + + FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) { + + if (dirent_is_file_with_suffix(de, ".journal") || + dirent_is_file_with_suffix(de, ".journal~")) + (void) add_file(j, m->path, de->d_name); + } + + check_network(j, dirfd(d)); + + return 0; + +fail: + k = journal_put_error(j, r, path ?: prefix); + if (k < 0) + return k; + + return r; +} + +static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { + + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + Directory *m; + int r, k; + + assert(j); + + /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we + * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially + * populate the set, as well as to update it later. */ + + if (p) { + /* If there's a path specified, use it. */ + + if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) && + !path_has_prefix(j, p, "/run")) + return -EINVAL; + + if (j->prefix) + p = strjoina(j->prefix, p); + + if (j->toplevel_fd < 0) + d = opendir(p); + else + d = xopendirat(j->toplevel_fd, skip_slash(p), 0); + + if (!d) { + if (errno == ENOENT && missing_ok) + return 0; + + r = log_debug_errno(errno, "Failed to open root directory %s: %m", p); + goto fail; + } + } else { + int dfd; + + /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since + * opendir() will take possession of the fd, and close it, which we don't want. */ + + p = "."; /* store this as "." in the directories hashmap */ + + dfd = fcntl(j->toplevel_fd, F_DUPFD_CLOEXEC, 3); + if (dfd < 0) { + r = -errno; + goto fail; + } + + d = fdopendir(dfd); + if (!d) { + r = -errno; + safe_close(dfd); + goto fail; + } + + rewinddir(d); + } + + m = hashmap_get(j->directories_by_path, p); + if (!m) { + m = new0(Directory, 1); + if (!m) { + r = -ENOMEM; + goto fail; + } + + m->is_root = true; + + m->path = strdup(p); + if (!m->path) { + free(m); + r = -ENOMEM; + goto fail; + } + + if (hashmap_put(j->directories_by_path, m->path, m) < 0) { + free(m->path); + free(m); + r = -ENOMEM; + goto fail; + } + + j->current_invalidate_counter++; + + log_debug("Root directory %s added.", m->path); + + } else if (!m->is_root) + return 0; + + if (m->wd <= 0 && j->inotify_fd >= 0) { + + m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d), + IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| + IN_ONLYDIR); + + if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0) + inotify_rm_watch(j->inotify_fd, m->wd); + } + + if (j->no_new_files) + return 0; + + FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) { + sd_id128_t id; + + if (dirent_is_file_with_suffix(de, ".journal") || + dirent_is_file_with_suffix(de, ".journal~")) + (void) add_file(j, m->path, de->d_name); + else if (IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN) && + sd_id128_from_string(de->d_name, &id) >= 0) + (void) add_directory(j, m->path, de->d_name); + } + + check_network(j, dirfd(d)); + + return 0; + +fail: + k = journal_put_error(j, r, p); + if (k < 0) + return k; + + return r; +} + +static void remove_directory(sd_journal *j, Directory *d) { + assert(j); + + if (d->wd > 0) { + hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd)); + + if (j->inotify_fd >= 0) + inotify_rm_watch(j->inotify_fd, d->wd); + } + + hashmap_remove(j->directories_by_path, d->path); + + if (d->is_root) + log_debug("Root directory %s removed.", d->path); + else + log_debug("Directory %s removed.", d->path); + + free(d->path); + free(d); +} + +static int add_search_paths(sd_journal *j) { + + static const char search_paths[] = + "/run/log/journal\0" + "/var/log/journal\0"; + const char *p; + + assert(j); + + /* We ignore most errors here, since the idea is to only open + * what's actually accessible, and ignore the rest. */ + + NULSTR_FOREACH(p, search_paths) + (void) add_root_directory(j, p, true); + + return 0; +} + +static int add_current_paths(sd_journal *j) { + Iterator i; + JournalFile *f; + + assert(j); + assert(j->no_new_files); + + /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we + * treat them as fatal. */ + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + _cleanup_free_ char *dir; + int r; + + dir = dirname_malloc(f->path); + if (!dir) + return -ENOMEM; + + r = add_directory(j, dir, NULL); + if (r < 0) + return r; + } + + return 0; +} + +static int allocate_inotify(sd_journal *j) { + assert(j); + + if (j->inotify_fd < 0) { + j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (j->inotify_fd < 0) + return -errno; + } + + return hashmap_ensure_allocated(&j->directories_by_wd, NULL); +} + +static sd_journal *journal_new(int flags, const char *path) { + sd_journal *j; + + j = new0(sd_journal, 1); + if (!j) + return NULL; + + j->original_pid = getpid(); + j->toplevel_fd = -1; + j->inotify_fd = -1; + j->flags = flags; + j->data_threshold = DEFAULT_DATA_THRESHOLD; + + if (path) { + j->path = strdup(path); + if (!j->path) + goto fail; + } + + j->files = ordered_hashmap_new(&string_hash_ops); + j->directories_by_path = hashmap_new(&string_hash_ops); + j->mmap = mmap_cache_new(); + if (!j->files || !j->directories_by_path || !j->mmap) + goto fail; + + return j; + +fail: + sd_journal_close(j); + return NULL; +} + +_public_ int sd_journal_open(sd_journal **ret, int flags) { + sd_journal *j; + int r; + + assert_return(ret, -EINVAL); + assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL); + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + r = add_search_paths(j); + if (r < 0) + goto fail; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + + return r; +} + +_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) { + _cleanup_free_ char *root = NULL, *class = NULL; + sd_journal *j; + char *p; + int r; + + /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in + * combination with sd_journal_open_directory_fd(). */ + + assert_return(machine, -EINVAL); + assert_return(ret, -EINVAL); + assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL); + assert_return(machine_name_is_valid(machine), -EINVAL); + + p = strjoina("/run/systemd/machines/", machine); + r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL); + if (r == -ENOENT) + return -EHOSTDOWN; + if (r < 0) + return r; + if (!root) + return -ENODATA; + + if (!streq_ptr(class, "container")) + return -EIO; + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + j->prefix = root; + root = NULL; + + r = add_search_paths(j); + if (r < 0) + goto fail; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) { + sd_journal *j; + int r; + + assert_return(ret, -EINVAL); + assert_return(path, -EINVAL); + assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); + + j = journal_new(flags, path); + if (!j) + return -ENOMEM; + + if (flags & SD_JOURNAL_OS_ROOT) + r = add_search_paths(j); + else + r = add_root_directory(j, path, false); + if (r < 0) + goto fail; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) { + sd_journal *j; + const char **path; + int r; + + assert_return(ret, -EINVAL); + assert_return(flags == 0, -EINVAL); + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + STRV_FOREACH(path, paths) { + r = add_any_file(j, -1, *path); + if (r < 0) + goto fail; + } + + j->no_new_files = true; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) { + sd_journal *j; + struct stat st; + int r; + + assert_return(ret, -EINVAL); + assert_return(fd >= 0, -EBADF); + assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode)) + return -EBADFD; + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + j->toplevel_fd = fd; + + if (flags & SD_JOURNAL_OS_ROOT) + r = add_search_paths(j); + else + r = add_root_directory(j, NULL, false); + if (r < 0) + goto fail; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) { + Iterator iterator; + JournalFile *f; + sd_journal *j; + unsigned i; + int r; + + assert_return(ret, -EINVAL); + assert_return(n_fds > 0, -EBADF); + assert_return(flags == 0, -EINVAL); + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + for (i = 0; i < n_fds; i++) { + struct stat st; + + if (fds[i] < 0) { + r = -EBADF; + goto fail; + } + + if (fstat(fds[i], &st) < 0) { + r = -errno; + goto fail; + } + + if (!S_ISREG(st.st_mode)) { + r = -EBADFD; + goto fail; + } + + r = add_any_file(j, fds[i], NULL); + if (r < 0) + goto fail; + } + + j->no_new_files = true; + j->no_inotify = true; + + *ret = j; + return 0; + +fail: + /* If we fail, make sure we don't take possession of the files we managed to make use of successfully, and they + * remain open */ + ORDERED_HASHMAP_FOREACH(f, j->files, iterator) + f->close_fd = false; + + sd_journal_close(j); + return r; +} + +_public_ void sd_journal_close(sd_journal *j) { + Directory *d; + JournalFile *f; + char *p; + + if (!j) + return; + + sd_journal_flush_matches(j); + + while ((f = ordered_hashmap_steal_first(j->files))) + (void) journal_file_close(f); + + ordered_hashmap_free(j->files); + + while ((d = hashmap_first(j->directories_by_path))) + remove_directory(j, d); + + while ((d = hashmap_first(j->directories_by_wd))) + remove_directory(j, d); + + hashmap_free(j->directories_by_path); + hashmap_free(j->directories_by_wd); + + safe_close(j->inotify_fd); + + if (j->mmap) { + log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap)); + mmap_cache_unref(j->mmap); + } + + while ((p = hashmap_steal_first(j->errors))) + free(p); + hashmap_free(j->errors); + + free(j->path); + free(j->prefix); + free(j->unique_field); + free(j->fields_buffer); + free(j); +} + +_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) { + Object *o; + JournalFile *f; + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(ret, -EINVAL); + + f = j->current_file; + if (!f) + return -EADDRNOTAVAIL; + + if (f->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + + *ret = le64toh(o->entry.realtime); + return 0; +} + +_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) { + Object *o; + JournalFile *f; + int r; + sd_id128_t id; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + f = j->current_file; + if (!f) + return -EADDRNOTAVAIL; + + if (f->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + + if (ret_boot_id) + *ret_boot_id = o->entry.boot_id; + else { + r = sd_id128_get_boot(&id); + if (r < 0) + return r; + + if (!sd_id128_equal(id, o->entry.boot_id)) + return -ESTALE; + } + + if (ret) + *ret = le64toh(o->entry.monotonic); + + return 0; +} + +static bool field_is_valid(const char *field) { + const char *p; + + assert(field); + + if (isempty(field)) + return false; + + if (startswith(field, "__")) + return false; + + for (p = field; *p; p++) { + + if (*p == '_') + continue; + + if (*p >= 'A' && *p <= 'Z') + continue; + + if (*p >= '0' && *p <= '9') + continue; + + return false; + } + + return true; +} + +_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) { + JournalFile *f; + uint64_t i, n; + size_t field_length; + int r; + Object *o; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(field, -EINVAL); + assert_return(data, -EINVAL); + assert_return(size, -EINVAL); + assert_return(field_is_valid(field), -EINVAL); + + f = j->current_file; + if (!f) + return -EADDRNOTAVAIL; + + if (f->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + + field_length = strlen(field); + + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) { + uint64_t p, l; + le64_t le_hash; + size_t t; + int compression; + + p = le64toh(o->entry.items[i].object_offset); + le_hash = o->entry.items[i].hash; + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + if (le_hash != o->data.hash) + return -EBADMSG; + + l = le64toh(o->object.size) - offsetof(Object, data.payload); + + compression = o->object.flags & OBJECT_COMPRESSION_MASK; + if (compression) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + r = decompress_startswith(compression, + o->data.payload, l, + &f->compress_buffer, &f->compress_buffer_size, + field, field_length, '='); + if (r < 0) + log_debug_errno(r, "Cannot decompress %s object of length %"PRIu64" at offset "OFSfmt": %m", + object_compressed_to_string(compression), l, p); + else if (r > 0) { + + size_t rsize; + + r = decompress_blob(compression, + o->data.payload, l, + &f->compress_buffer, &f->compress_buffer_size, &rsize, + j->data_threshold); + if (r < 0) + return r; + + *data = f->compress_buffer; + *size = (size_t) rsize; + + return 0; + } +#else + return -EPROTONOSUPPORT; +#endif + } else if (l >= field_length+1 && + memcmp(o->data.payload, field, field_length) == 0 && + o->data.payload[field_length] == '=') { + + t = (size_t) l; + + if ((uint64_t) t != l) + return -E2BIG; + + *data = o->data.payload; + *size = t; + + return 0; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + } + + return -ENOENT; +} + +static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) { + size_t t; + uint64_t l; + int compression; + + l = le64toh(o->object.size) - offsetof(Object, data.payload); + t = (size_t) l; + + /* We can't read objects larger than 4G on a 32bit machine */ + if ((uint64_t) t != l) + return -E2BIG; + + compression = o->object.flags & OBJECT_COMPRESSION_MASK; + if (compression) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + size_t rsize; + int r; + + r = decompress_blob(compression, + o->data.payload, l, &f->compress_buffer, + &f->compress_buffer_size, &rsize, j->data_threshold); + if (r < 0) + return r; + + *data = f->compress_buffer; + *size = (size_t) rsize; +#else + return -EPROTONOSUPPORT; +#endif + } else { + *data = o->data.payload; + *size = t; + } + + return 0; +} + +_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { + JournalFile *f; + uint64_t p, n; + le64_t le_hash; + int r; + Object *o; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(data, -EINVAL); + assert_return(size, -EINVAL); + + f = j->current_file; + if (!f) + return -EADDRNOTAVAIL; + + if (f->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + + n = journal_file_entry_n_items(o); + if (j->current_field >= n) + return 0; + + p = le64toh(o->entry.items[j->current_field].object_offset); + le_hash = o->entry.items[j->current_field].hash; + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + if (le_hash != o->data.hash) + return -EBADMSG; + + r = return_data(j, f, o, data, size); + if (r < 0) + return r; + + j->current_field++; + + return 1; +} + +_public_ void sd_journal_restart_data(sd_journal *j) { + if (!j) + return; + + j->current_field = 0; +} + +_public_ int sd_journal_get_fd(sd_journal *j) { + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + if (j->no_inotify) + return -EMEDIUMTYPE; + + if (j->inotify_fd >= 0) + return j->inotify_fd; + + r = allocate_inotify(j); + if (r < 0) + return r; + + log_debug("Reiterating files to get inotify watches established"); + + /* Iterate through all dirs again, to add them to the + * inotify */ + if (j->no_new_files) + r = add_current_paths(j); + else if (j->toplevel_fd >= 0) + r = add_root_directory(j, NULL, false); + else if (j->path) + r = add_root_directory(j, j->path, true); + else + r = add_search_paths(j); + if (r < 0) + return r; + + return j->inotify_fd; +} + +_public_ int sd_journal_get_events(sd_journal *j) { + int fd; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + fd = sd_journal_get_fd(j); + if (fd < 0) + return fd; + + return POLLIN; +} + +_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) { + int fd; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(timeout_usec, -EINVAL); + + fd = sd_journal_get_fd(j); + if (fd < 0) + return fd; + + if (!j->on_network) { + *timeout_usec = (uint64_t) -1; + return 0; + } + + /* If we are on the network we need to regularly check for + * changes manually */ + + *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC; + return 1; +} + +static void process_inotify_event(sd_journal *j, struct inotify_event *e) { + Directory *d; + + assert(j); + assert(e); + + /* Is this a subdirectory we watch? */ + d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd)); + if (d) { + sd_id128_t id; + + if (!(e->mask & IN_ISDIR) && e->len > 0 && + (endswith(e->name, ".journal") || + endswith(e->name, ".journal~"))) { + + /* Event for a journal file */ + + if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) + (void) add_file(j, d->path, e->name); + else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) + remove_file(j, d->path, e->name); + + } else if (!d->is_root && e->len == 0) { + + /* Event for a subdirectory */ + + if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) + remove_directory(j, d); + + } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) { + + /* Event for root directory */ + + if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) + (void) add_directory(j, d->path, e->name); + } + + return; + } + + if (e->mask & IN_IGNORED) + return; + + log_debug("Unknown inotify event."); +} + +static int determine_change(sd_journal *j) { + bool b; + + assert(j); + + b = j->current_invalidate_counter != j->last_invalidate_counter; + j->last_invalidate_counter = j->current_invalidate_counter; + + return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND; +} + +_public_ int sd_journal_process(sd_journal *j) { + bool got_something = false; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + j->last_process_usec = now(CLOCK_MONOTONIC); + + for (;;) { + union inotify_event_buffer buffer; + struct inotify_event *e; + ssize_t l; + + l = read(j->inotify_fd, &buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EAGAIN || errno == EINTR) + return got_something ? determine_change(j) : SD_JOURNAL_NOP; + + return -errno; + } + + got_something = true; + + FOREACH_INOTIFY_EVENT(e, buffer, l) + process_inotify_event(j, e); + } +} + +_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) { + int r; + uint64_t t; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + if (j->inotify_fd < 0) { + + /* This is the first invocation, hence create the + * inotify watch */ + r = sd_journal_get_fd(j); + if (r < 0) + return r; + + /* The journal might have changed since the context + * object was created and we weren't watching before, + * hence don't wait for anything, and return + * immediately. */ + return determine_change(j); + } + + r = sd_journal_get_timeout(j, &t); + if (r < 0) + return r; + + if (t != (uint64_t) -1) { + usec_t n; + + n = now(CLOCK_MONOTONIC); + t = t > n ? t - n : 0; + + if (timeout_usec == (uint64_t) -1 || timeout_usec > t) + timeout_usec = t; + } + + do { + r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec); + } while (r == -EINTR); + + if (r < 0) + return r; + + return sd_journal_process(j); +} + +_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) { + Iterator i; + JournalFile *f; + bool first = true; + uint64_t fmin = 0, tmax = 0; + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(from || to, -EINVAL); + assert_return(from != to, -EINVAL); + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + usec_t fr, t; + + r = journal_file_get_cutoff_realtime_usec(f, &fr, &t); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + if (r == 0) + continue; + + if (first) { + fmin = fr; + tmax = t; + first = false; + } else { + fmin = MIN(fr, fmin); + tmax = MAX(t, tmax); + } + } + + if (from) + *from = fmin; + if (to) + *to = tmax; + + return first ? 0 : 1; +} + +_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) { + Iterator i; + JournalFile *f; + bool found = false; + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(from || to, -EINVAL); + assert_return(from != to, -EINVAL); + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + usec_t fr, t; + + r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + if (r == 0) + continue; + + if (found) { + if (from) + *from = MIN(fr, *from); + if (to) + *to = MAX(t, *to); + } else { + if (from) + *from = fr; + if (to) + *to = t; + found = true; + } + } + + return found; +} + +void journal_print_header(sd_journal *j) { + Iterator i; + JournalFile *f; + bool newline = false; + + assert(j); + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + if (newline) + putchar('\n'); + else + newline = true; + + journal_file_print_header(f); + } +} + +_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) { + Iterator i; + JournalFile *f; + uint64_t sum = 0; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(bytes, -EINVAL); + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + struct stat st; + + if (fstat(f->fd, &st) < 0) + return -errno; + + sum += (uint64_t) st.st_blocks * 512ULL; + } + + *bytes = sum; + return 0; +} + +_public_ int sd_journal_query_unique(sd_journal *j, const char *field) { + char *f; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(!isempty(field), -EINVAL); + assert_return(field_is_valid(field), -EINVAL); + + f = strdup(field); + if (!f) + return -ENOMEM; + + free(j->unique_field); + j->unique_field = f; + j->unique_file = NULL; + j->unique_offset = 0; + j->unique_file_lost = false; + + return 0; +} + +_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { + size_t k; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(data, -EINVAL); + assert_return(l, -EINVAL); + assert_return(j->unique_field, -EINVAL); + + k = strlen(j->unique_field); + + if (!j->unique_file) { + if (j->unique_file_lost) + return 0; + + j->unique_file = ordered_hashmap_first(j->files); + if (!j->unique_file) + return 0; + + j->unique_offset = 0; + } + + for (;;) { + JournalFile *of; + Iterator i; + Object *o; + const void *odata; + size_t ol; + bool found; + int r; + + /* Proceed to next data object in the field's linked list */ + if (j->unique_offset == 0) { + r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL); + if (r < 0) + return r; + + j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0; + } else { + r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o); + if (r < 0) + return r; + + j->unique_offset = le64toh(o->data.next_field_offset); + } + + /* We reached the end of the list? Then start again, with the next file */ + if (j->unique_offset == 0) { + j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path); + if (!j->unique_file) + return 0; + + continue; + } + + /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED + * instead, so that we can look at this data object at the same + * time as one on another file */ + r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o); + if (r < 0) + return r; + + /* Let's do the type check by hand, since we used 0 context above. */ + if (o->object.type != OBJECT_DATA) { + log_debug("%s:offset " OFSfmt ": object has type %d, expected %d", + j->unique_file->path, j->unique_offset, + o->object.type, OBJECT_DATA); + return -EBADMSG; + } + + r = return_data(j, j->unique_file, o, &odata, &ol); + if (r < 0) + return r; + + /* Check if we have at least the field name and "=". */ + if (ol <= k) { + log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu", + j->unique_file->path, j->unique_offset, + ol, k + 1); + return -EBADMSG; + } + + if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') { + log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"", + j->unique_file->path, j->unique_offset, + j->unique_field); + return -EBADMSG; + } + + /* OK, now let's see if we already returned this data + * object by checking if it exists in the earlier + * traversed files. */ + found = false; + ORDERED_HASHMAP_FOREACH(of, j->files, i) { + if (of == j->unique_file) + break; + + /* Skip this file it didn't have any fields indexed */ + if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0) + continue; + + r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), NULL, NULL); + if (r < 0) + return r; + if (r > 0) { + found = true; + break; + } + } + + if (found) + continue; + + r = return_data(j, j->unique_file, o, data, l); + if (r < 0) + return r; + + return 1; + } +} + +_public_ void sd_journal_restart_unique(sd_journal *j) { + if (!j) + return; + + j->unique_file = NULL; + j->unique_offset = 0; + j->unique_file_lost = false; +} + +_public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) { + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(field, -EINVAL); + + if (!j->fields_file) { + if (j->fields_file_lost) + return 0; + + j->fields_file = ordered_hashmap_first(j->files); + if (!j->fields_file) + return 0; + + j->fields_hash_table_index = 0; + j->fields_offset = 0; + } + + for (;;) { + JournalFile *f, *of; + Iterator i; + uint64_t m; + Object *o; + size_t sz; + bool found; + + f = j->fields_file; + + if (j->fields_offset == 0) { + bool eof = false; + + /* We are not yet positioned at any field. Let's pick the first one */ + r = journal_file_map_field_hash_table(f); + if (r < 0) + return r; + + m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem); + for (;;) { + if (j->fields_hash_table_index >= m) { + /* Reached the end of the hash table, go to the next file. */ + eof = true; + break; + } + + j->fields_offset = le64toh(f->field_hash_table[j->fields_hash_table_index].head_hash_offset); + + if (j->fields_offset != 0) + break; + + /* Empty hash table bucket, go to next one */ + j->fields_hash_table_index++; + } + + if (eof) { + /* Proceed with next file */ + j->fields_file = ordered_hashmap_next(j->files, f->path); + if (!j->fields_file) { + *field = NULL; + return 0; + } + + j->fields_offset = 0; + j->fields_hash_table_index = 0; + continue; + } + + } else { + /* We are already positioned at a field. If so, let's figure out the next field from it */ + + r = journal_file_move_to_object(f, OBJECT_FIELD, j->fields_offset, &o); + if (r < 0) + return r; + + j->fields_offset = le64toh(o->field.next_hash_offset); + if (j->fields_offset == 0) { + /* Reached the end of the hash table chain */ + j->fields_hash_table_index++; + continue; + } + } + + /* We use OBJECT_UNUSED here, so that the iterator below doesn't remove our mmap window */ + r = journal_file_move_to_object(f, OBJECT_UNUSED, j->fields_offset, &o); + if (r < 0) + return r; + + /* Because we used OBJECT_UNUSED above, we need to do our type check manually */ + if (o->object.type != OBJECT_FIELD) { + log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD); + return -EBADMSG; + } + + sz = le64toh(o->object.size) - offsetof(Object, field.payload); + + /* Let's see if we already returned this field name before. */ + found = false; + ORDERED_HASHMAP_FOREACH(of, j->files, i) { + if (of == f) + break; + + /* Skip this file it didn't have any fields indexed */ + if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0) + continue; + + r = journal_file_find_field_object_with_hash(of, o->field.payload, sz, le64toh(o->field.hash), NULL, NULL); + if (r < 0) + return r; + if (r > 0) { + found = true; + break; + } + } + + if (found) + continue; + + /* Check if this is really a valid string containing no NUL byte */ + if (memchr(o->field.payload, 0, sz)) + return -EBADMSG; + + if (sz > j->data_threshold) + sz = j->data_threshold; + + if (!GREEDY_REALLOC(j->fields_buffer, j->fields_buffer_allocated, sz + 1)) + return -ENOMEM; + + memcpy(j->fields_buffer, o->field.payload, sz); + j->fields_buffer[sz] = 0; + + if (!field_is_valid(j->fields_buffer)) + return -EBADMSG; + + *field = j->fields_buffer; + return 1; + } +} + +_public_ void sd_journal_restart_fields(sd_journal *j) { + if (!j) + return; + + j->fields_file = NULL; + j->fields_hash_table_index = 0; + j->fields_offset = 0; + j->fields_file_lost = false; +} + +_public_ int sd_journal_reliable_fd(sd_journal *j) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + return !j->on_network; +} + +static char *lookup_field(const char *field, void *userdata) { + sd_journal *j = userdata; + const void *data; + size_t size, d; + int r; + + assert(field); + assert(j); + + r = sd_journal_get_data(j, field, &data, &size); + if (r < 0 || + size > REPLACE_VAR_MAX) + return strdup(field); + + d = strlen(field) + 1; + + return strndup((const char*) data + d, size - d); +} + +_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) { + const void *data; + size_t size; + sd_id128_t id; + _cleanup_free_ char *text = NULL, *cid = NULL; + char *t; + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(ret, -EINVAL); + + r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size); + if (r < 0) + return r; + + cid = strndup((const char*) data + 11, size - 11); + if (!cid) + return -ENOMEM; + + r = sd_id128_from_string(cid, &id); + if (r < 0) + return r; + + r = catalog_get(CATALOG_DATABASE, id, &text); + if (r < 0) + return r; + + t = replace_var(text, lookup_field, j); + if (!t) + return -ENOMEM; + + *ret = t; + return 0; +} + +_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) { + assert_return(ret, -EINVAL); + + return catalog_get(CATALOG_DATABASE, id, ret); +} + +_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + j->data_threshold = sz; + return 0; +} + +_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(sz, -EINVAL); + + *sz = j->data_threshold; + return 0; +} + +_public_ int sd_journal_has_runtime_files(sd_journal *j) { + assert_return(j, -EINVAL); + + return j->has_runtime_files; +} + +_public_ int sd_journal_has_persistent_files(sd_journal *j) { + assert_return(j, -EINVAL); + + return j->has_persistent_files; +} diff --git a/src/libsystemd/src/sd-login/sd-login.c b/src/libsystemd/src/sd-login/sd-login.c new file mode 100644 index 0000000000..84831d5e95 --- /dev/null +++ b/src/libsystemd/src/sd-login/sd-login.c @@ -0,0 +1,1062 @@ +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "cgroup-util.h" +#include "dirent-util.h" +#include "escape.h" +#include "fd-util.h" +#include "fileio.h" +#include "formats-util.h" +#include "fs-util.h" +#include "hostname-util.h" +#include "io-util.h" +#include "login-util.h" +#include "macro.h" +#include "parse-util.h" +#include "path-util.h" +#include "socket-util.h" +#include "string-util.h" +#include "strv.h" +#include "user-util.h" +#include "util.h" + +/* Error codes: + * + * invalid input parameters → -EINVAL + * invalid fd → -EBADF + * process does not exist → -ESRCH + * cgroup does not exist → -ENOENT + * machine, session does not exist → -ENXIO + * requested metadata on object is missing → -ENODATA + */ + +_public_ int sd_pid_get_session(pid_t pid, char **session) { + + assert_return(pid >= 0, -EINVAL); + assert_return(session, -EINVAL); + + return cg_pid_get_session(pid, session); +} + +_public_ int sd_pid_get_unit(pid_t pid, char **unit) { + + assert_return(pid >= 0, -EINVAL); + assert_return(unit, -EINVAL); + + return cg_pid_get_unit(pid, unit); +} + +_public_ int sd_pid_get_user_unit(pid_t pid, char **unit) { + + assert_return(pid >= 0, -EINVAL); + assert_return(unit, -EINVAL); + + return cg_pid_get_user_unit(pid, unit); +} + +_public_ int sd_pid_get_machine_name(pid_t pid, char **name) { + + assert_return(pid >= 0, -EINVAL); + assert_return(name, -EINVAL); + + return cg_pid_get_machine_name(pid, name); +} + +_public_ int sd_pid_get_slice(pid_t pid, char **slice) { + + assert_return(pid >= 0, -EINVAL); + assert_return(slice, -EINVAL); + + return cg_pid_get_slice(pid, slice); +} + +_public_ int sd_pid_get_user_slice(pid_t pid, char **slice) { + + assert_return(pid >= 0, -EINVAL); + assert_return(slice, -EINVAL); + + return cg_pid_get_user_slice(pid, slice); +} + +_public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) { + + assert_return(pid >= 0, -EINVAL); + assert_return(uid, -EINVAL); + + return cg_pid_get_owner_uid(pid, uid); +} + +_public_ int sd_pid_get_cgroup(pid_t pid, char **cgroup) { + char *c; + int r; + + assert_return(pid >= 0, -EINVAL); + assert_return(cgroup, -EINVAL); + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &c); + if (r < 0) + return r; + + /* The internal APIs return the empty string for the root + * cgroup, let's return the "/" in the public APIs instead, as + * that's easier and less ambigious for people to grok. */ + if (isempty(c)) { + free(c); + c = strdup("/"); + if (!c) + return -ENOMEM; + + } + + *cgroup = c; + return 0; +} + +_public_ int sd_peer_get_session(int fd, char **session) { + struct ucred ucred = {}; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(session, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_session(ucred.pid, session); +} + +_public_ int sd_peer_get_owner_uid(int fd, uid_t *uid) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(uid, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_owner_uid(ucred.pid, uid); +} + +_public_ int sd_peer_get_unit(int fd, char **unit) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(unit, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_unit(ucred.pid, unit); +} + +_public_ int sd_peer_get_user_unit(int fd, char **unit) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(unit, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_user_unit(ucred.pid, unit); +} + +_public_ int sd_peer_get_machine_name(int fd, char **machine) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(machine, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_machine_name(ucred.pid, machine); +} + +_public_ int sd_peer_get_slice(int fd, char **slice) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(slice, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_slice(ucred.pid, slice); +} + +_public_ int sd_peer_get_user_slice(int fd, char **slice) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(slice, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_user_slice(ucred.pid, slice); +} + +_public_ int sd_peer_get_cgroup(int fd, char **cgroup) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(cgroup, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return sd_pid_get_cgroup(ucred.pid, cgroup); +} + +static int file_of_uid(uid_t uid, char **p) { + + assert_return(uid_is_valid(uid), -EINVAL); + assert(p); + + if (asprintf(p, "/run/systemd/users/" UID_FMT, uid) < 0) + return -ENOMEM; + + return 0; +} + +_public_ int sd_uid_get_state(uid_t uid, char**state) { + _cleanup_free_ char *p = NULL; + char *s = NULL; + int r; + + assert_return(state, -EINVAL); + + r = file_of_uid(uid, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, "STATE", &s, NULL); + if (r == -ENOENT) { + free(s); + s = strdup("offline"); + if (!s) + return -ENOMEM; + + } + if (r < 0) { + free(s); + return r; + } + if (isempty(s)) { + free(s); + return -EIO; + } + + *state = s; + return 0; +} + +_public_ int sd_uid_get_display(uid_t uid, char **session) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + assert_return(session, -EINVAL); + + r = file_of_uid(uid, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, "DISPLAY", &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + *session = s; + s = NULL; + + return 0; +} + +static int file_of_seat(const char *seat, char **_p) { + char *p; + int r; + + assert(_p); + + if (seat) { + if (!filename_is_valid(seat)) + return -EINVAL; + + p = strappend("/run/systemd/seats/", seat); + } else { + _cleanup_free_ char *buf = NULL; + + r = sd_session_get_seat(NULL, &buf); + if (r < 0) + return r; + + p = strappend("/run/systemd/seats/", buf); + } + + if (!p) + return -ENOMEM; + + *_p = p; + p = NULL; + return 0; +} + +_public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) { + _cleanup_free_ char *t = NULL, *s = NULL, *p = NULL; + size_t l; + int r; + const char *word, *variable, *state; + + assert_return(uid_is_valid(uid), -EINVAL); + + r = file_of_seat(seat, &p); + if (r < 0) + return r; + + variable = require_active ? "ACTIVE_UID" : "UIDS"; + + r = parse_env_file(p, NEWLINE, variable, &s, NULL); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + if (isempty(s)) + return 0; + + if (asprintf(&t, UID_FMT, uid) < 0) + return -ENOMEM; + + FOREACH_WORD(word, l, s, state) + if (strneq(t, word, l)) + return 1; + + return 0; +} + +static int uid_get_array(uid_t uid, const char *variable, char ***array) { + _cleanup_free_ char *p = NULL, *s = NULL; + char **a; + int r; + + assert(variable); + + r = file_of_uid(uid, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, variable, &s, NULL); + if (r == -ENOENT || (r >= 0 && isempty(s))) { + if (array) + *array = NULL; + return 0; + } + if (r < 0) + return r; + + a = strv_split(s, " "); + if (!a) + return -ENOMEM; + + strv_uniq(a); + r = strv_length(a); + + if (array) + *array = a; + else + strv_free(a); + + return r; +} + +_public_ int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions) { + return uid_get_array( + uid, + require_active == 0 ? "ONLINE_SESSIONS" : + require_active > 0 ? "ACTIVE_SESSIONS" : + "SESSIONS", + sessions); +} + +_public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) { + return uid_get_array( + uid, + require_active == 0 ? "ONLINE_SEATS" : + require_active > 0 ? "ACTIVE_SEATS" : + "SEATS", + seats); +} + +static int file_of_session(const char *session, char **_p) { + char *p; + int r; + + assert(_p); + + if (session) { + if (!session_id_valid(session)) + return -EINVAL; + + p = strappend("/run/systemd/sessions/", session); + } else { + _cleanup_free_ char *buf = NULL; + + r = sd_pid_get_session(0, &buf); + if (r < 0) + return r; + + p = strappend("/run/systemd/sessions/", buf); + } + + if (!p) + return -ENOMEM; + + *_p = p; + return 0; +} + +_public_ int sd_session_is_active(const char *session) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + r = file_of_session(session, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (isempty(s)) + return -EIO; + + return parse_boolean(s); +} + +_public_ int sd_session_is_remote(const char *session) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + r = file_of_session(session, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, "REMOTE", &s, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + return parse_boolean(s); +} + +_public_ int sd_session_get_state(const char *session, char **state) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + assert_return(state, -EINVAL); + + r = file_of_session(session, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, "STATE", &s, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (isempty(s)) + return -EIO; + + *state = s; + s = NULL; + + return 0; +} + +_public_ int sd_session_get_uid(const char *session, uid_t *uid) { + int r; + _cleanup_free_ char *p = NULL, *s = NULL; + + assert_return(uid, -EINVAL); + + r = file_of_session(session, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, "UID", &s, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (isempty(s)) + return -EIO; + + return parse_uid(s, uid); +} + +static int session_get_string(const char *session, const char *field, char **value) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + assert_return(value, -EINVAL); + assert(field); + + r = file_of_session(session, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, field, &s, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + *value = s; + s = NULL; + return 0; +} + +_public_ int sd_session_get_seat(const char *session, char **seat) { + return session_get_string(session, "SEAT", seat); +} + +_public_ int sd_session_get_tty(const char *session, char **tty) { + return session_get_string(session, "TTY", tty); +} + +_public_ int sd_session_get_vt(const char *session, unsigned *vtnr) { + _cleanup_free_ char *vtnr_string = NULL; + unsigned u; + int r; + + assert_return(vtnr, -EINVAL); + + r = session_get_string(session, "VTNR", &vtnr_string); + if (r < 0) + return r; + + r = safe_atou(vtnr_string, &u); + if (r < 0) + return r; + + *vtnr = u; + return 0; +} + +_public_ int sd_session_get_service(const char *session, char **service) { + return session_get_string(session, "SERVICE", service); +} + +_public_ int sd_session_get_type(const char *session, char **type) { + return session_get_string(session, "TYPE", type); +} + +_public_ int sd_session_get_class(const char *session, char **class) { + return session_get_string(session, "CLASS", class); +} + +_public_ int sd_session_get_desktop(const char *session, char **desktop) { + _cleanup_free_ char *escaped = NULL; + char *t; + int r; + + assert_return(desktop, -EINVAL); + + r = session_get_string(session, "DESKTOP", &escaped); + if (r < 0) + return r; + + r = cunescape(escaped, 0, &t); + if (r < 0) + return r; + + *desktop = t; + return 0; +} + +_public_ int sd_session_get_display(const char *session, char **display) { + return session_get_string(session, "DISPLAY", display); +} + +_public_ int sd_session_get_remote_user(const char *session, char **remote_user) { + return session_get_string(session, "REMOTE_USER", remote_user); +} + +_public_ int sd_session_get_remote_host(const char *session, char **remote_host) { + return session_get_string(session, "REMOTE_HOST", remote_host); +} + +_public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) { + _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL; + int r; + + assert_return(session || uid, -EINVAL); + + r = file_of_seat(seat, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, + "ACTIVE", &s, + "ACTIVE_UID", &t, + NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + + if (session && !s) + return -ENODATA; + + if (uid && !t) + return -ENODATA; + + if (uid && t) { + r = parse_uid(t, uid); + if (r < 0) + return r; + } + + if (session && s) { + *session = s; + s = NULL; + } + + return 0; +} + +_public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) { + _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL; + _cleanup_strv_free_ char **a = NULL; + _cleanup_free_ uid_t *b = NULL; + unsigned n = 0; + int r; + + r = file_of_seat(seat, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, + "SESSIONS", &s, + "ACTIVE_SESSIONS", &t, + NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + + if (s) { + a = strv_split(s, " "); + if (!a) + return -ENOMEM; + } + + if (uids && t) { + const char *word, *state; + size_t l; + + FOREACH_WORD(word, l, t, state) + n++; + + if (n > 0) { + unsigned i = 0; + + b = new(uid_t, n); + if (!b) + return -ENOMEM; + + FOREACH_WORD(word, l, t, state) { + _cleanup_free_ char *k = NULL; + + k = strndup(word, l); + if (!k) + return -ENOMEM; + + r = parse_uid(k, b + i); + if (r < 0) + continue; + + i++; + } + } + } + + r = strv_length(a); + + if (sessions) { + *sessions = a; + a = NULL; + } + + if (uids) { + *uids = b; + b = NULL; + } + + if (n_uids) + *n_uids = n; + + return r; +} + +static int seat_get_can(const char *seat, const char *variable) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + assert(variable); + + r = file_of_seat(seat, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, + variable, &s, + NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + return parse_boolean(s); +} + +_public_ int sd_seat_can_multi_session(const char *seat) { + return seat_get_can(seat, "CAN_MULTI_SESSION"); +} + +_public_ int sd_seat_can_tty(const char *seat) { + return seat_get_can(seat, "CAN_TTY"); +} + +_public_ int sd_seat_can_graphical(const char *seat) { + return seat_get_can(seat, "CAN_GRAPHICAL"); +} + +_public_ int sd_get_seats(char ***seats) { + return get_files_in_directory("/run/systemd/seats/", seats); +} + +_public_ int sd_get_sessions(char ***sessions) { + return get_files_in_directory("/run/systemd/sessions/", sessions); +} + +_public_ int sd_get_uids(uid_t **users) { + _cleanup_closedir_ DIR *d; + int r = 0; + unsigned n = 0; + _cleanup_free_ uid_t *l = NULL; + + d = opendir("/run/systemd/users/"); + if (!d) + return -errno; + + for (;;) { + struct dirent *de; + int k; + uid_t uid; + + errno = 0; + de = readdir(d); + if (!de && errno > 0) + return -errno; + + if (!de) + break; + + dirent_ensure_type(d, de); + + if (!dirent_is_file(de)) + continue; + + k = parse_uid(de->d_name, &uid); + if (k < 0) + continue; + + if (users) { + if ((unsigned) r >= n) { + uid_t *t; + + n = MAX(16, 2*r); + t = realloc(l, sizeof(uid_t) * n); + if (!t) + return -ENOMEM; + + l = t; + } + + assert((unsigned) r < n); + l[r++] = uid; + } else + r++; + } + + if (users) { + *users = l; + l = NULL; + } + + return r; +} + +_public_ int sd_get_machine_names(char ***machines) { + char **l = NULL, **a, **b; + int r; + + assert_return(machines, -EINVAL); + + r = get_files_in_directory("/run/systemd/machines/", &l); + if (r < 0) + return r; + + if (l) { + r = 0; + + /* Filter out the unit: symlinks */ + for (a = l, b = l; *a; a++) { + if (startswith(*a, "unit:") || !machine_name_is_valid(*a)) + free(*a); + else { + *b = *a; + b++; + r++; + } + } + + *b = NULL; + } + + *machines = l; + return r; +} + +_public_ int sd_machine_get_class(const char *machine, char **class) { + _cleanup_free_ char *c = NULL; + const char *p; + int r; + + assert_return(machine_name_is_valid(machine), -EINVAL); + assert_return(class, -EINVAL); + + p = strjoina("/run/systemd/machines/", machine); + r = parse_env_file(p, NEWLINE, "CLASS", &c, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (!c) + return -EIO; + + *class = c; + c = NULL; + + return 0; +} + +_public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) { + _cleanup_free_ char *netif = NULL; + size_t l, allocated = 0, nr = 0; + int *ni = NULL; + const char *p, *word, *state; + int r; + + assert_return(machine_name_is_valid(machine), -EINVAL); + assert_return(ifindices, -EINVAL); + + p = strjoina("/run/systemd/machines/", machine); + r = parse_env_file(p, NEWLINE, "NETIF", &netif, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (!netif) { + *ifindices = NULL; + return 0; + } + + FOREACH_WORD(word, l, netif, state) { + char buf[l+1]; + int ifi; + + *(char*) (mempcpy(buf, word, l)) = 0; + + if (parse_ifindex(buf, &ifi) < 0) + continue; + + if (!GREEDY_REALLOC(ni, allocated, nr+1)) { + free(ni); + return -ENOMEM; + } + + ni[nr++] = ifi; + } + + *ifindices = ni; + return nr; +} + +static inline int MONITOR_TO_FD(sd_login_monitor *m) { + return (int) (unsigned long) m - 1; +} + +static inline sd_login_monitor* FD_TO_MONITOR(int fd) { + return (sd_login_monitor*) (unsigned long) (fd + 1); +} + +_public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) { + int fd, k; + bool good = false; + + assert_return(m, -EINVAL); + + fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (fd < 0) + return -errno; + + if (!category || streq(category, "seat")) { + k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE); + if (k < 0) { + safe_close(fd); + return -errno; + } + + good = true; + } + + if (!category || streq(category, "session")) { + k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE); + if (k < 0) { + safe_close(fd); + return -errno; + } + + good = true; + } + + if (!category || streq(category, "uid")) { + k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE); + if (k < 0) { + safe_close(fd); + return -errno; + } + + good = true; + } + + if (!category || streq(category, "machine")) { + k = inotify_add_watch(fd, "/run/systemd/machines/", IN_MOVED_TO|IN_DELETE); + if (k < 0) { + safe_close(fd); + return -errno; + } + + good = true; + } + + if (!good) { + close_nointr(fd); + return -EINVAL; + } + + *m = FD_TO_MONITOR(fd); + return 0; +} + +_public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) { + int fd; + + if (!m) + return NULL; + + fd = MONITOR_TO_FD(m); + close_nointr(fd); + + return NULL; +} + +_public_ int sd_login_monitor_flush(sd_login_monitor *m) { + + assert_return(m, -EINVAL); + + return flush_fd(MONITOR_TO_FD(m)); +} + +_public_ int sd_login_monitor_get_fd(sd_login_monitor *m) { + + assert_return(m, -EINVAL); + + return MONITOR_TO_FD(m); +} + +_public_ int sd_login_monitor_get_events(sd_login_monitor *m) { + + assert_return(m, -EINVAL); + + /* For now we will only return POLLIN here, since we don't + * need anything else ever for inotify. However, let's have + * this API to keep our options open should we later on need + * it. */ + return POLLIN; +} + +_public_ int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec) { + + assert_return(m, -EINVAL); + assert_return(timeout_usec, -EINVAL); + + /* For now we will only return (uint64_t) -1, since we don't + * need any timeout. However, let's have this API to keep our + * options open should we later on need it. */ + *timeout_usec = (uint64_t) -1; + return 0; +} diff --git a/src/libsystemd/src/sd-login/test-login.c b/src/libsystemd/src/sd-login/test-login.c new file mode 100644 index 0000000000..994c76df4a --- /dev/null +++ b/src/libsystemd/src/sd-login/test-login.c @@ -0,0 +1,264 @@ +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +#include +#include + +#include + +#include "alloc-util.h" +#include "fd-util.h" +#include "formats-util.h" +#include "string-util.h" +#include "strv.h" +#include "util.h" + +static void test_login(void) { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_free_ char *pp = NULL, *qq = NULL; + int r, k; + uid_t u, u2; + char *seat, *type, *class, *display, *remote_user, *remote_host, *display_session, *cgroup; + char *session; + char *state; + char *session2; + char *t; + char **seats, **sessions, **machines; + uid_t *uids; + unsigned n; + struct pollfd pollfd; + sd_login_monitor *m = NULL; + + assert_se(sd_pid_get_session(0, &session) == 0); + printf("session = %s\n", session); + + assert_se(sd_pid_get_owner_uid(0, &u2) == 0); + printf("user = "UID_FMT"\n", u2); + + assert_se(sd_pid_get_cgroup(0, &cgroup) == 0); + printf("cgroup = %s\n", cgroup); + free(cgroup); + + display_session = NULL; + r = sd_uid_get_display(u2, &display_session); + assert_se(r >= 0 || r == -ENODATA); + printf("user's display session = %s\n", strna(display_session)); + free(display_session); + + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0); + sd_peer_get_session(pair[0], &pp); + sd_peer_get_session(pair[1], &qq); + assert_se(streq_ptr(pp, qq)); + + r = sd_uid_get_sessions(u2, false, &sessions); + assert_se(r >= 0); + assert_se(r == (int) strv_length(sessions)); + assert_se(t = strv_join(sessions, ", ")); + strv_free(sessions); + printf("sessions = %s\n", t); + free(t); + + assert_se(r == sd_uid_get_sessions(u2, false, NULL)); + + r = sd_uid_get_seats(u2, false, &seats); + assert_se(r >= 0); + assert_se(r == (int) strv_length(seats)); + assert_se(t = strv_join(seats, ", ")); + strv_free(seats); + printf("seats = %s\n", t); + free(t); + + assert_se(r == sd_uid_get_seats(u2, false, NULL)); + + r = sd_session_is_active(session); + assert_se(r >= 0); + printf("active = %s\n", yes_no(r)); + + r = sd_session_is_remote(session); + assert_se(r >= 0); + printf("remote = %s\n", yes_no(r)); + + r = sd_session_get_state(session, &state); + assert_se(r >= 0); + printf("state = %s\n", state); + free(state); + + assert_se(sd_session_get_uid(session, &u) >= 0); + printf("uid = "UID_FMT"\n", u); + assert_se(u == u2); + + assert_se(sd_session_get_type(session, &type) >= 0); + printf("type = %s\n", type); + free(type); + + assert_se(sd_session_get_class(session, &class) >= 0); + printf("class = %s\n", class); + free(class); + + display = NULL; + r = sd_session_get_display(session, &display); + assert_se(r >= 0 || r == -ENODATA); + printf("display = %s\n", strna(display)); + free(display); + + remote_user = NULL; + r = sd_session_get_remote_user(session, &remote_user); + assert_se(r >= 0 || r == -ENODATA); + printf("remote_user = %s\n", strna(remote_user)); + free(remote_user); + + remote_host = NULL; + r = sd_session_get_remote_host(session, &remote_host); + assert_se(r >= 0 || r == -ENODATA); + printf("remote_host = %s\n", strna(remote_host)); + free(remote_host); + + assert_se(sd_session_get_seat(session, &seat) >= 0); + printf("seat = %s\n", seat); + + r = sd_seat_can_multi_session(seat); + assert_se(r >= 0); + printf("can do multi session = %s\n", yes_no(r)); + + r = sd_seat_can_tty(seat); + assert_se(r >= 0); + printf("can do tty = %s\n", yes_no(r)); + + r = sd_seat_can_graphical(seat); + assert_se(r >= 0); + printf("can do graphical = %s\n", yes_no(r)); + + assert_se(sd_uid_get_state(u, &state) >= 0); + printf("state = %s\n", state); + + assert_se(sd_uid_is_on_seat(u, 0, seat) > 0); + + k = sd_uid_is_on_seat(u, 1, seat); + assert_se(k >= 0); + assert_se(!!r == !!r); + + assert_se(sd_seat_get_active(seat, &session2, &u2) >= 0); + printf("session2 = %s\n", session2); + printf("uid2 = "UID_FMT"\n", u2); + + r = sd_seat_get_sessions(seat, &sessions, &uids, &n); + assert_se(r >= 0); + printf("n_sessions = %i\n", r); + assert_se(r == (int) strv_length(sessions)); + assert_se(t = strv_join(sessions, ", ")); + strv_free(sessions); + printf("sessions = %s\n", t); + free(t); + printf("uids ="); + for (k = 0; k < (int) n; k++) + printf(" "UID_FMT, uids[k]); + printf("\n"); + free(uids); + + assert_se(sd_seat_get_sessions(seat, NULL, NULL, NULL) == r); + + free(session); + free(state); + free(session2); + free(seat); + + r = sd_get_seats(&seats); + assert_se(r >= 0); + assert_se(r == (int) strv_length(seats)); + assert_se(t = strv_join(seats, ", ")); + strv_free(seats); + printf("n_seats = %i\n", r); + printf("seats = %s\n", t); + free(t); + + assert_se(sd_get_seats(NULL) == r); + + r = sd_seat_get_active(NULL, &t, NULL); + assert_se(r >= 0); + printf("active session on current seat = %s\n", t); + free(t); + + r = sd_get_sessions(&sessions); + assert_se(r >= 0); + assert_se(r == (int) strv_length(sessions)); + assert_se(t = strv_join(sessions, ", ")); + strv_free(sessions); + printf("n_sessions = %i\n", r); + printf("sessions = %s\n", t); + free(t); + + assert_se(sd_get_sessions(NULL) == r); + + r = sd_get_uids(&uids); + assert_se(r >= 0); + + printf("uids ="); + for (k = 0; k < r; k++) + printf(" "UID_FMT, uids[k]); + printf("\n"); + free(uids); + + printf("n_uids = %i\n", r); + assert_se(sd_get_uids(NULL) == r); + + r = sd_get_machine_names(&machines); + assert_se(r >= 0); + assert_se(r == (int) strv_length(machines)); + assert_se(t = strv_join(machines, ", ")); + strv_free(machines); + printf("n_machines = %i\n", r); + printf("machines = %s\n", t); + free(t); + + r = sd_login_monitor_new("session", &m); + assert_se(r >= 0); + + for (n = 0; n < 5; n++) { + usec_t timeout, nw; + + zero(pollfd); + assert_se((pollfd.fd = sd_login_monitor_get_fd(m)) >= 0); + assert_se((pollfd.events = sd_login_monitor_get_events(m)) >= 0); + + assert_se(sd_login_monitor_get_timeout(m, &timeout) >= 0); + + nw = now(CLOCK_MONOTONIC); + + r = poll(&pollfd, 1, + timeout == (uint64_t) -1 ? -1 : + timeout > nw ? (int) ((timeout - nw) / 1000) : + 0); + + assert_se(r >= 0); + + sd_login_monitor_flush(m); + printf("Wake!\n"); + } + + sd_login_monitor_unref(m); +} + +int main(int argc, char* argv[]) { + log_parse_environment(); + log_open(); + + test_login(); + + return 0; +} diff --git a/src/libsystemd/src/sd-netlink/local-addresses.c b/src/libsystemd/src/sd-netlink/local-addresses.c new file mode 100644 index 0000000000..1abce75b01 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/local-addresses.c @@ -0,0 +1,275 @@ +/*** + This file is part of systemd. + + Copyright 2008-2011 Lennart Poettering + Copyright 2014 Tom Gundersen + + 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 . +***/ + +#include + +#include "alloc-util.h" +#include "local-addresses.h" +#include "macro.h" +#include "netlink-util.h" + +static int address_compare(const void *_a, const void *_b) { + const struct local_address *a = _a, *b = _b; + + /* Order lowest scope first, IPv4 before IPv6, lowest interface index first */ + + if (a->family == AF_INET && b->family == AF_INET6) + return -1; + if (a->family == AF_INET6 && b->family == AF_INET) + return 1; + + if (a->scope < b->scope) + return -1; + if (a->scope > b->scope) + return 1; + + if (a->metric < b->metric) + return -1; + if (a->metric > b->metric) + return 1; + + if (a->ifindex < b->ifindex) + return -1; + if (a->ifindex > b->ifindex) + return 1; + + return memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(a->family)); +} + +int local_addresses(sd_netlink *context, int ifindex, int af, struct local_address **ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_free_ struct local_address *list = NULL; + size_t n_list = 0, n_allocated = 0; + sd_netlink_message *m; + int r; + + assert(ret); + + if (context) + rtnl = sd_netlink_ref(context); + else { + r = sd_netlink_open(&rtnl); + if (r < 0) + return r; + } + + r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, af); + if (r < 0) + return r; + + r = sd_netlink_call(rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (m = reply; m; m = sd_netlink_message_next(m)) { + struct local_address *a; + unsigned char flags; + uint16_t type; + int ifi, family; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + return r; + + r = sd_netlink_message_get_type(m, &type); + if (r < 0) + return r; + if (type != RTM_NEWADDR) + continue; + + r = sd_rtnl_message_addr_get_ifindex(m, &ifi); + if (r < 0) + return r; + if (ifindex > 0 && ifi != ifindex) + continue; + + r = sd_rtnl_message_addr_get_family(m, &family); + if (r < 0) + return r; + if (af != AF_UNSPEC && af != family) + continue; + + r = sd_rtnl_message_addr_get_flags(m, &flags); + if (r < 0) + return r; + if (flags & IFA_F_DEPRECATED) + continue; + + if (!GREEDY_REALLOC0(list, n_allocated, n_list+1)) + return -ENOMEM; + + a = list + n_list; + + r = sd_rtnl_message_addr_get_scope(m, &a->scope); + if (r < 0) + return r; + + if (ifindex == 0 && (a->scope == RT_SCOPE_HOST || a->scope == RT_SCOPE_NOWHERE)) + continue; + + switch (family) { + + case AF_INET: + r = sd_netlink_message_read_in_addr(m, IFA_LOCAL, &a->address.in); + if (r < 0) { + r = sd_netlink_message_read_in_addr(m, IFA_ADDRESS, &a->address.in); + if (r < 0) + continue; + } + break; + + case AF_INET6: + r = sd_netlink_message_read_in6_addr(m, IFA_LOCAL, &a->address.in6); + if (r < 0) { + r = sd_netlink_message_read_in6_addr(m, IFA_ADDRESS, &a->address.in6); + if (r < 0) + continue; + } + break; + + default: + continue; + } + + a->ifindex = ifi; + a->family = family; + + n_list++; + }; + + qsort_safe(list, n_list, sizeof(struct local_address), address_compare); + + *ret = list; + list = NULL; + + return (int) n_list; +} + +int local_gateways(sd_netlink *context, int ifindex, int af, struct local_address **ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_free_ struct local_address *list = NULL; + sd_netlink_message *m = NULL; + size_t n_list = 0, n_allocated = 0; + int r; + + assert(ret); + + if (context) + rtnl = sd_netlink_ref(context); + else { + r = sd_netlink_open(&rtnl); + if (r < 0) + return r; + } + + r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, af, RTPROT_UNSPEC); + if (r < 0) + return r; + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_netlink_call(rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (m = reply; m; m = sd_netlink_message_next(m)) { + struct local_address *a; + uint16_t type; + unsigned char dst_len, src_len; + uint32_t ifi; + int family; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + return r; + + r = sd_netlink_message_get_type(m, &type); + if (r < 0) + return r; + if (type != RTM_NEWROUTE) + continue; + + /* We only care for default routes */ + r = sd_rtnl_message_route_get_dst_prefixlen(m, &dst_len); + if (r < 0) + return r; + if (dst_len != 0) + continue; + + r = sd_rtnl_message_route_get_src_prefixlen(m, &src_len); + if (r < 0) + return r; + if (src_len != 0) + continue; + + r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi); + if (r < 0) + return r; + if (ifindex > 0 && (int) ifi != ifindex) + continue; + + r = sd_rtnl_message_route_get_family(m, &family); + if (r < 0) + return r; + if (af != AF_UNSPEC && af != family) + continue; + + if (!GREEDY_REALLOC0(list, n_allocated, n_list + 1)) + return -ENOMEM; + + a = list + n_list; + + switch (family) { + case AF_INET: + r = sd_netlink_message_read_in_addr(m, RTA_GATEWAY, &a->address.in); + if (r < 0) + continue; + + break; + case AF_INET6: + r = sd_netlink_message_read_in6_addr(m, RTA_GATEWAY, &a->address.in6); + if (r < 0) + continue; + + break; + default: + continue; + } + + sd_netlink_message_read_u32(m, RTA_PRIORITY, &a->metric); + + a->ifindex = ifi; + a->family = family; + + n_list++; + } + + if (n_list > 0) + qsort(list, n_list, sizeof(struct local_address), address_compare); + + *ret = list; + list = NULL; + + return (int) n_list; +} diff --git a/src/libsystemd/src/sd-netlink/local-addresses.h b/src/libsystemd/src/sd-netlink/local-addresses.h new file mode 100644 index 0000000000..1ddc50ace5 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/local-addresses.h @@ -0,0 +1,36 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2008-2011 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 . +***/ + + +#include + +#include "in-addr-util.h" + +struct local_address { + int family, ifindex; + unsigned char scope; + uint32_t metric; + union in_addr_union address; +}; + +int local_addresses(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret); + +int local_gateways(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret); diff --git a/src/libsystemd/src/sd-netlink/netlink-internal.h b/src/libsystemd/src/sd-netlink/netlink-internal.h new file mode 100644 index 0000000000..1d29c3a369 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-internal.h @@ -0,0 +1,137 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include + +#include + +#include "list.h" +#include "netlink-types.h" +#include "prioq.h" +#include "refcnt.h" + +#define RTNL_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC)) + +#define RTNL_WQUEUE_MAX 1024 +#define RTNL_RQUEUE_MAX 64*1024 + +#define RTNL_CONTAINER_DEPTH 32 + +struct reply_callback { + sd_netlink_message_handler_t callback; + void *userdata; + usec_t timeout; + uint64_t serial; + unsigned prioq_idx; +}; + +struct match_callback { + sd_netlink_message_handler_t callback; + uint16_t type; + void *userdata; + + LIST_FIELDS(struct match_callback, match_callbacks); +}; + +struct sd_netlink { + RefCount n_ref; + + int fd; + + union { + struct sockaddr sa; + struct sockaddr_nl nl; + } sockaddr; + + Hashmap *broadcast_group_refs; + bool broadcast_group_dont_leave:1; /* until we can rely on 4.2 */ + + sd_netlink_message **rqueue; + unsigned rqueue_size; + size_t rqueue_allocated; + + sd_netlink_message **rqueue_partial; + unsigned rqueue_partial_size; + size_t rqueue_partial_allocated; + + struct nlmsghdr *rbuffer; + size_t rbuffer_allocated; + + 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; +}; + +struct netlink_attribute { + size_t offset; /* offset from hdr to attribute */ + bool nested:1; + bool net_byteorder:1; +}; + +struct netlink_container { + const struct NLTypeSystem *type_system; /* the type system of the container */ + size_t offset; /* offset from hdr to the start of the container */ + struct netlink_attribute *attributes; + unsigned short n_attributes; /* number of attributes in container */ +}; + +struct sd_netlink_message { + RefCount n_ref; + + sd_netlink *rtnl; + + struct nlmsghdr *hdr; + struct netlink_container containers[RTNL_CONTAINER_DEPTH]; + unsigned n_containers; /* number of containers */ + bool sealed:1; + bool broadcast:1; + + sd_netlink_message *next; /* next in a chain of multi-part messages */ +}; + +int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type); +int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret); + +int socket_open(int family); +int socket_bind(sd_netlink *nl); +int socket_broadcast_group_ref(sd_netlink *nl, unsigned group); +int socket_broadcast_group_unref(sd_netlink *nl, unsigned group); +int socket_write_message(sd_netlink *nl, sd_netlink_message *m); +int socket_read_message(sd_netlink *nl); + +int rtnl_rqueue_make_room(sd_netlink *rtnl); +int rtnl_rqueue_partial_make_room(sd_netlink *rtnl); + +/* Make sure callbacks don't destroy the rtnl connection */ +#define NETLINK_DONT_DESTROY(rtnl) \ + _cleanup_(sd_netlink_unrefp) _unused_ sd_netlink *_dont_destroy_##rtnl = sd_netlink_ref(rtnl) diff --git a/src/libsystemd/src/sd-netlink/netlink-message.c b/src/libsystemd/src/sd-netlink/netlink-message.c new file mode 100644 index 0000000000..c00785ea41 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-message.c @@ -0,0 +1,961 @@ +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "formats-util.h" +#include "missing.h" +#include "netlink-internal.h" +#include "netlink-types.h" +#include "netlink-util.h" +#include "refcnt.h" +#include "socket-util.h" +#include "util.h" + +#define GET_CONTAINER(m, i) ((i) < (m)->n_containers ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset) : NULL) +#define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr; + +#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK) +#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK) + +int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) { + sd_netlink_message *m; + + assert_return(ret, -EINVAL); + + /* Note that 'rtnl' is currently unused, if we start using it internally + we must take care to avoid problems due to mutual references between + buses and their queued messages. See sd-bus. + */ + + m = new0(sd_netlink_message, 1); + if (!m) + return -ENOMEM; + + m->n_ref = REFCNT_INIT; + + m->sealed = false; + + *ret = m; + + return 0; +} + +int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + const NLType *nl_type; + size_t size; + int r; + + r = type_system_get_type(&type_system_root, &nl_type, type); + if (r < 0) + return r; + + if (type_get_type(nl_type) != NETLINK_TYPE_NESTED) + return -EINVAL; + + r = message_new_empty(rtnl, &m); + if (r < 0) + return r; + + size = NLMSG_SPACE(type_get_size(nl_type)); + + assert(size >= sizeof(struct nlmsghdr)); + m->hdr = malloc0(size); + if (!m->hdr) + return -ENOMEM; + + m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + + type_get_type_system(nl_type, &m->containers[0].type_system); + m->hdr->nlmsg_len = size; + m->hdr->nlmsg_type = type; + + *ret = m; + m = NULL; + + return 0; +} + +int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(m->hdr->nlmsg_type == RTM_GETLINK || + m->hdr->nlmsg_type == RTM_GETADDR || + m->hdr->nlmsg_type == RTM_GETROUTE || + m->hdr->nlmsg_type == RTM_GETNEIGH, + -EINVAL); + + SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump); + + return 0; +} + +sd_netlink_message *sd_netlink_message_ref(sd_netlink_message *m) { + if (m) + assert_se(REFCNT_INC(m->n_ref) >= 2); + + return m; +} + +sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m) { + if (m && REFCNT_DEC(m->n_ref) == 0) { + unsigned i; + + free(m->hdr); + + for (i = 0; i <= m->n_containers; i++) + free(m->containers[i].attributes); + + sd_netlink_message_unref(m->next); + + free(m); + } + + return NULL; +} + +int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) { + assert_return(m, -EINVAL); + assert_return(type, -EINVAL); + + *type = m->hdr->nlmsg_type; + + return 0; +} + +int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags) { + assert_return(m, -EINVAL); + assert_return(flags, -EINVAL); + + m->hdr->nlmsg_flags = flags; + + return 0; +} + +int sd_netlink_message_is_broadcast(sd_netlink_message *m) { + assert_return(m, -EINVAL); + + return m->broadcast; +} + +/* If successful the updated message will be correctly aligned, if + unsuccessful the old message is untouched. */ +static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *data, size_t data_length) { + uint32_t rta_length; + size_t message_length, padding_length; + struct nlmsghdr *new_hdr; + struct rtattr *rta; + char *padding; + unsigned i; + int offset; + + assert(m); + assert(m->hdr); + assert(!m->sealed); + assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len); + assert(!data || data_length); + + /* get offset of the new attribute */ + offset = m->hdr->nlmsg_len; + + /* 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 = offset + 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 + offset); + + /* if we are inside containers, extend them */ + for (i = 0; i < m->n_containers; i++) + GET_CONTAINER(m, i)->rta_len += message_length - offset; + + /* fill in the attribute */ + rta->rta_type = type; + rta->rta_len = rta_length; + if (data) + /* 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); + + else + /* if no data was passed, make sure we still initialize the padding + note that we can have data_length > 0 (used by some containers) */ + padding = RTA_DATA(rta); + + /* make sure also the padding at the end of the message is initialized */ + padding_length = (uint8_t*)m->hdr + message_length - (uint8_t*)padding; + memzero(padding, padding_length); + + /* update message size */ + m->hdr->nlmsg_len = message_length; + + return offset; +} + +static int message_attribute_has_type(sd_netlink_message *m, size_t *out_size, uint16_t attribute_type, uint16_t data_type) { + const NLType *type; + int r; + + assert(m); + + r = type_system_get_type(m->containers[m->n_containers].type_system, &type, attribute_type); + if (r < 0) + return r; + + if (type_get_type(type) != data_type) + return -EINVAL; + + if (out_size) + *out_size = type_get_size(type); + return 0; +} + +int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data) { + size_t length, size; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(data, -EINVAL); + + r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING); + if (r < 0) + return r; + + if (size) { + length = strnlen(data, size+1); + if (length > size) + return -EINVAL; + } else + length = strlen(data); + + r = add_rtattr(m, type, data, length + 1); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type) { + size_t size; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_FLAG); + if (r < 0) + return r; + + r = add_rtattr(m, type, NULL, 0); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8); + if (r < 0) + return r; + + r = add_rtattr(m, type, &data, sizeof(uint8_t)); + if (r < 0) + return r; + + return 0; +} + + +int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16); + if (r < 0) + return r; + + r = add_rtattr(m, type, &data, sizeof(uint16_t)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32); + if (r < 0) + return r; + + r = add_rtattr(m, type, &data, sizeof(uint32_t)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = add_rtattr(m, type, data, len); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(data, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); + if (r < 0) + return r; + + r = add_rtattr(m, type, data, sizeof(struct in_addr)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(data, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); + if (r < 0) + return r; + + r = add_rtattr(m, type, data, sizeof(struct in6_addr)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(data, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); + if (r < 0) + return r; + + r = add_rtattr(m, type, data, ETH_ALEN); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(info, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO); + if (r < 0) + return r; + + r = add_rtattr(m, type, info, sizeof(struct ifa_cacheinfo)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type) { + size_t size; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -ERANGE); + + r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_NESTED); + if (r < 0) { + const NLTypeSystemUnion *type_system_union; + int family; + + r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_UNION); + if (r < 0) + return r; + + r = sd_rtnl_message_get_family(m, &family); + if (r < 0) + return r; + + r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type); + if (r < 0) + return r; + + r = type_system_union_protocol_get_type_system(type_system_union, + &m->containers[m->n_containers + 1].type_system, + family); + if (r < 0) + return r; + } else { + r = type_system_get_type_system(m->containers[m->n_containers].type_system, + &m->containers[m->n_containers + 1].type_system, + type); + if (r < 0) + return r; + } + + r = add_rtattr(m, type | NLA_F_NESTED, NULL, size); + if (r < 0) + return r; + + m->containers[m->n_containers++].offset = r; + + return 0; +} + +int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key) { + const NLTypeSystemUnion *type_system_union; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type); + if (r < 0) + return r; + + r = type_system_union_get_type_system(type_system_union, + &m->containers[m->n_containers + 1].type_system, + key); + if (r < 0) + return r; + + r = sd_netlink_message_append_string(m, type_system_union->match, key); + if (r < 0) + return r; + + /* do we evere need non-null size */ + r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0); + if (r < 0) + return r; + + m->containers[m->n_containers++].offset = r; + + return 0; +} + + +int sd_netlink_message_close_container(sd_netlink_message *m) { + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(m->n_containers > 0, -EINVAL); + + m->containers[m->n_containers].type_system = NULL; + m->n_containers--; + + return 0; +} + +static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data, bool *net_byteorder) { + struct netlink_attribute *attribute; + struct rtattr *rta; + + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + assert_return(data, -EINVAL); + assert(m->n_containers < RTNL_CONTAINER_DEPTH); + assert(m->containers[m->n_containers].attributes); + assert(type < m->containers[m->n_containers].n_attributes); + + attribute = &m->containers[m->n_containers].attributes[type]; + + if (!attribute->offset) + return -ENODATA; + + rta = (struct rtattr*)((uint8_t *) m->hdr + attribute->offset); + + *data = RTA_DATA(rta); + + if (net_byteorder) + *net_byteorder = attribute->net_byteorder; + + return RTA_PAYLOAD(rta); +} + +int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if (strnlen(attr_data, r) >= (size_t) r) + return -EIO; + + if (data) + *data = (const char *) attr_data; + + return 0; +} + +int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if ((size_t) r < sizeof(uint8_t)) + return -EIO; + + if (data) + *data = *(uint8_t *) attr_data; + + return 0; +} + +int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) { + void *attr_data; + bool net_byteorder; + int r; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); + if (r < 0) + return r; + else if ((size_t) r < sizeof(uint16_t)) + return -EIO; + + if (data) { + if (net_byteorder) + *data = be16toh(*(uint16_t *) attr_data); + else + *data = *(uint16_t *) attr_data; + } + + return 0; +} + +int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) { + void *attr_data; + bool net_byteorder; + int r; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); + if (r < 0) + return r; + else if ((size_t)r < sizeof(uint32_t)) + return -EIO; + + if (data) { + if (net_byteorder) + *data = be32toh(*(uint32_t *) attr_data); + else + *data = *(uint32_t *) attr_data; + } + + return 0; +} + +int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if ((size_t)r < sizeof(struct ether_addr)) + return -EIO; + + if (data) + memcpy(data, attr_data, sizeof(struct ether_addr)); + + return 0; +} + +int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if ((size_t)r < sizeof(struct ifa_cacheinfo)) + return -EIO; + + if (info) + memcpy(info, attr_data, sizeof(struct ifa_cacheinfo)); + + return 0; +} + +int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if ((size_t)r < sizeof(struct in_addr)) + return -EIO; + + if (data) + memcpy(data, attr_data, sizeof(struct in_addr)); + + return 0; +} + +int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if ((size_t)r < sizeof(struct in6_addr)) + return -EIO; + + if (data) + memcpy(data, attr_data, sizeof(struct in6_addr)); + + return 0; +} + +static int netlink_container_parse(sd_netlink_message *m, + struct netlink_container *container, + int count, + struct rtattr *rta, + unsigned int rt_len) { + _cleanup_free_ struct netlink_attribute *attributes = NULL; + + attributes = new0(struct netlink_attribute, count); + if (!attributes) + return -ENOMEM; + + for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) { + unsigned short type; + + type = RTA_TYPE(rta); + + /* if the kernel is newer than the headers we used + when building, we ignore out-of-range attributes */ + if (type >= count) + continue; + + if (attributes[type].offset) + log_debug("rtnl: message parse - overwriting repeated attribute"); + + attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr; + attributes[type].nested = RTA_FLAGS(rta) & NLA_F_NESTED; + attributes[type].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER; + } + + container->attributes = attributes; + attributes = NULL; + container->n_attributes = count; + + return 0; +} + +int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type_id) { + const NLType *nl_type; + const NLTypeSystem *type_system; + void *container; + uint16_t type; + size_t size; + int r; + + assert_return(m, -EINVAL); + assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -EINVAL); + + r = type_system_get_type(m->containers[m->n_containers].type_system, + &nl_type, + type_id); + if (r < 0) + return r; + + type = type_get_type(nl_type); + + if (type == NETLINK_TYPE_NESTED) { + r = type_system_get_type_system(m->containers[m->n_containers].type_system, + &type_system, + type_id); + if (r < 0) + return r; + } else if (type == NETLINK_TYPE_UNION) { + const NLTypeSystemUnion *type_system_union; + + r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, + &type_system_union, + type_id); + if (r < 0) + return r; + + switch (type_system_union->match_type) { + case NL_MATCH_SIBLING: + { + const char *key; + + r = sd_netlink_message_read_string(m, type_system_union->match, &key); + if (r < 0) + return r; + + r = type_system_union_get_type_system(type_system_union, + &type_system, + key); + if (r < 0) + return r; + + break; + } + case NL_MATCH_PROTOCOL: + { + int family; + + r = sd_rtnl_message_get_family(m, &family); + if (r < 0) + return r; + + r = type_system_union_protocol_get_type_system(type_system_union, + &type_system, + family); + if (r < 0) + return r; + + break; + } + default: + assert_not_reached("sd-netlink: invalid type system union type"); + } + } else + return -EINVAL; + + r = netlink_message_read_internal(m, type_id, &container, NULL); + if (r < 0) + return r; + else + size = (size_t)r; + + m->n_containers++; + + r = netlink_container_parse(m, + &m->containers[m->n_containers], + type_system_get_count(type_system), + container, + size); + if (r < 0) { + m->n_containers--; + return r; + } + + m->containers[m->n_containers].type_system = type_system; + + return 0; +} + +int sd_netlink_message_exit_container(sd_netlink_message *m) { + assert_return(m, -EINVAL); + assert_return(m->sealed, -EINVAL); + assert_return(m->n_containers > 0, -EINVAL); + + m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes); + m->containers[m->n_containers].type_system = NULL; + + m->n_containers--; + + return 0; +} + +uint32_t rtnl_message_get_serial(sd_netlink_message *m) { + assert(m); + assert(m->hdr); + + return m->hdr->nlmsg_seq; +} + +int sd_netlink_message_is_error(sd_netlink_message *m) { + assert_return(m, 0); + assert_return(m->hdr, 0); + + return m->hdr->nlmsg_type == NLMSG_ERROR; +} + +int sd_netlink_message_get_errno(sd_netlink_message *m) { + struct nlmsgerr *err; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + + if (!sd_netlink_message_is_error(m)) + return 0; + + err = NLMSG_DATA(m->hdr); + + return err->error; +} + +int sd_netlink_message_rewind(sd_netlink_message *m) { + const NLType *nl_type; + uint16_t type; + size_t size; + unsigned i; + int r; + + assert_return(m, -EINVAL); + + /* don't allow appending to message once parsed */ + if (!m->sealed) + rtnl_message_seal(m); + + for (i = 1; i <= m->n_containers; i++) + m->containers[i].attributes = mfree(m->containers[i].attributes); + + m->n_containers = 0; + + if (m->containers[0].attributes) + /* top-level attributes have already been parsed */ + return 0; + + assert(m->hdr); + + r = type_system_get_type(&type_system_root, &nl_type, m->hdr->nlmsg_type); + if (r < 0) + return r; + + type = type_get_type(nl_type); + size = type_get_size(nl_type); + + if (type == NETLINK_TYPE_NESTED) { + const NLTypeSystem *type_system; + + type_get_type_system(nl_type, &type_system); + + m->containers[0].type_system = type_system; + + r = netlink_container_parse(m, + &m->containers[m->n_containers], + type_system_get_count(type_system), + (struct rtattr*)((uint8_t*)NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)), + NLMSG_PAYLOAD(m->hdr, size)); + if (r < 0) + return r; + } + + return 0; +} + +void rtnl_message_seal(sd_netlink_message *m) { + assert(m); + assert(!m->sealed); + + m->sealed = true; +} + +sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m) { + assert_return(m, NULL); + + return m->next; +} diff --git a/src/libsystemd/src/sd-netlink/netlink-socket.c b/src/libsystemd/src/sd-netlink/netlink-socket.c new file mode 100644 index 0000000000..d28a413c65 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-socket.c @@ -0,0 +1,474 @@ +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "formats-util.h" +#include "missing.h" +#include "netlink-internal.h" +#include "netlink-types.h" +#include "netlink-util.h" +#include "refcnt.h" +#include "socket-util.h" +#include "util.h" + +int socket_open(int family) { + int fd; + + fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family); + if (fd < 0) + return -errno; + + return fd; +} + +static int broadcast_groups_get(sd_netlink *nl) { + _cleanup_free_ uint32_t *groups = NULL; + socklen_t len = 0, old_len; + unsigned i, j; + int r; + + assert(nl); + assert(nl->fd >= 0); + + r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len); + if (r < 0) { + if (errno == ENOPROTOOPT) { + nl->broadcast_group_dont_leave = true; + return 0; + } else + return -errno; + } + + if (len == 0) + return 0; + + groups = new0(uint32_t, len); + if (!groups) + return -ENOMEM; + + old_len = len; + + r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, groups, &len); + if (r < 0) + return -errno; + + if (old_len != len) + return -EIO; + + r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL); + if (r < 0) + return r; + + for (i = 0; i < len; i++) { + for (j = 0; j < sizeof(uint32_t) * 8; j++) { + uint32_t offset; + unsigned group; + + offset = 1U << j; + + if (!(groups[i] & offset)) + continue; + + group = i * sizeof(uint32_t) * 8 + j + 1; + + r = hashmap_put(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(1)); + if (r < 0) + return r; + } + } + + return 0; +} + +int socket_bind(sd_netlink *nl) { + socklen_t addrlen; + int r, one = 1; + + r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one)); + if (r < 0) + return -errno; + + addrlen = sizeof(nl->sockaddr); + + r = bind(nl->fd, &nl->sockaddr.sa, addrlen); + /* ignore EINVAL to allow opening an already bound socket */ + if (r < 0 && errno != EINVAL) + return -errno; + + r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen); + if (r < 0) + return -errno; + + r = broadcast_groups_get(nl); + if (r < 0) + return r; + + return 0; +} + +static unsigned broadcast_group_get_ref(sd_netlink *nl, unsigned group) { + assert(nl); + + return PTR_TO_UINT(hashmap_get(nl->broadcast_group_refs, UINT_TO_PTR(group))); +} + +static int broadcast_group_set_ref(sd_netlink *nl, unsigned group, unsigned n_ref) { + int r; + + assert(nl); + + r = hashmap_replace(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(n_ref)); + if (r < 0) + return r; + + return 0; +} + +static int broadcast_group_join(sd_netlink *nl, unsigned group) { + int r; + + assert(nl); + assert(nl->fd >= 0); + assert(group > 0); + + r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); + if (r < 0) + return -errno; + + return 0; +} + +int socket_broadcast_group_ref(sd_netlink *nl, unsigned group) { + unsigned n_ref; + int r; + + assert(nl); + + n_ref = broadcast_group_get_ref(nl, group); + + n_ref++; + + r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL); + if (r < 0) + return r; + + r = broadcast_group_set_ref(nl, group, n_ref); + if (r < 0) + return r; + + if (n_ref > 1) + /* not yet in the group */ + return 0; + + r = broadcast_group_join(nl, group); + if (r < 0) + return r; + + return 0; +} + +static int broadcast_group_leave(sd_netlink *nl, unsigned group) { + int r; + + assert(nl); + assert(nl->fd >= 0); + assert(group > 0); + + if (nl->broadcast_group_dont_leave) + return 0; + + r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group)); + if (r < 0) + return -errno; + + return 0; +} + +int socket_broadcast_group_unref(sd_netlink *nl, unsigned group) { + unsigned n_ref; + int r; + + assert(nl); + + n_ref = broadcast_group_get_ref(nl, group); + + assert(n_ref > 0); + + n_ref--; + + r = broadcast_group_set_ref(nl, group, n_ref); + if (r < 0) + return r; + + if (n_ref > 0) + /* still refs left */ + return 0; + + r = broadcast_group_leave(nl, group); + if (r < 0) + return r; + + return 0; +} + +/* returns the number of bytes sent, or a negative error code */ +int socket_write_message(sd_netlink *nl, sd_netlink_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; + + return k; +} + +static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) { + union sockaddr_union sender; + uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))]; + struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = 1, + .msg_name = &sender, + .msg_namelen = sizeof(sender), + .msg_control = cmsg_buffer, + .msg_controllen = sizeof(cmsg_buffer), + }; + struct cmsghdr *cmsg; + uint32_t group = 0; + int r; + + assert(fd >= 0); + assert(iov); + + r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0)); + if (r < 0) { + /* no data */ + if (errno == ENOBUFS) + log_debug("rtnl: kernel receive buffer overrun"); + else if (errno == EAGAIN) + log_debug("rtnl: no data in socket"); + + return (errno == EAGAIN || errno == EINTR) ? 0 : -errno; + } + + if (sender.nl.nl_pid != 0) { + /* not from the kernel, ignore */ + log_debug("rtnl: ignoring message from portid %"PRIu32, sender.nl.nl_pid); + + if (peek) { + /* drop the message */ + r = recvmsg(fd, &msg, 0); + if (r < 0) + return (errno == EAGAIN || errno == EINTR) ? 0 : -errno; + } + + return 0; + } + + CMSG_FOREACH(cmsg, &msg) { + if (cmsg->cmsg_level == SOL_NETLINK && + cmsg->cmsg_type == NETLINK_PKTINFO && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) { + struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg); + + /* multi-cast group */ + group = pktinfo->group; + } + } + + if (_group) + *_group = group; + + return r; +} + +/* 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_netlink *rtnl) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL; + struct iovec iov = {}; + uint32_t group = 0; + bool multi_part = false, done = false; + struct nlmsghdr *new_msg; + size_t len; + int r; + unsigned i = 0; + + assert(rtnl); + assert(rtnl->rbuffer); + assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr)); + + /* read nothing, just get the pending message size */ + r = socket_recv_message(rtnl->fd, &iov, NULL, true); + if (r <= 0) + return r; + else + len = (size_t)r; + + /* make room for the pending message */ + if (!greedy_realloc((void **)&rtnl->rbuffer, + &rtnl->rbuffer_allocated, + len, sizeof(uint8_t))) + return -ENOMEM; + + iov.iov_base = rtnl->rbuffer; + iov.iov_len = rtnl->rbuffer_allocated; + + /* read the pending message */ + r = socket_recv_message(rtnl->fd, &iov, &group, false); + if (r <= 0) + return r; + else + len = (size_t)r; + + if (len > rtnl->rbuffer_allocated) + /* message did not fit in read buffer */ + return -EIO; + + if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) { + multi_part = true; + + for (i = 0; i < rtnl->rqueue_partial_size; i++) { + if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) == + rtnl->rbuffer->nlmsg_seq) { + first = rtnl->rqueue_partial[i]; + break; + } + } + } + + for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + const NLType *nl_type; + + if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid) + /* not broadcast and not for us */ + continue; + + if (new_msg->nlmsg_type == NLMSG_NOOP) + /* silently drop noop messages */ + continue; + + if (new_msg->nlmsg_type == NLMSG_DONE) { + /* finished reading multi-part message */ + done = true; + + /* if first is not defined, put NLMSG_DONE into the receive queue. */ + if (first) + continue; + } + + /* check that we support this message type */ + r = type_system_get_type(&type_system_root, &nl_type, new_msg->nlmsg_type); + if (r < 0) { + if (r == -EOPNOTSUPP) + log_debug("sd-netlink: ignored message with unknown type: %i", + new_msg->nlmsg_type); + + continue; + } + + /* check that the size matches the message type */ + if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) { + log_debug("sd-netlink: message larger than expected, dropping"); + continue; + } + + r = message_new_empty(rtnl, &m); + if (r < 0) + return r; + + m->broadcast = !!group; + + m->hdr = memdup(new_msg, new_msg->nlmsg_len); + if (!m->hdr) + return -ENOMEM; + + /* seal and parse the top-level message */ + r = sd_netlink_message_rewind(m); + if (r < 0) + return r; + + /* push the message onto the multi-part message stack */ + if (first) + m->next = first; + first = m; + m = NULL; + } + + if (len) + log_debug("sd-netlink: discarding %zu bytes of incoming message", len); + + if (!first) + return 0; + + if (!multi_part || done) { + /* we got a complete message, push it on the read queue */ + r = rtnl_rqueue_make_room(rtnl); + if (r < 0) + return r; + + rtnl->rqueue[rtnl->rqueue_size++] = first; + first = NULL; + + if (multi_part && (i < rtnl->rqueue_partial_size)) { + /* remove the message form the partial read queue */ + memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1, + sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1)); + rtnl->rqueue_partial_size--; + } + + return 1; + } else { + /* we only got a partial multi-part message, push it on the + partial read queue */ + if (i < rtnl->rqueue_partial_size) { + rtnl->rqueue_partial[i] = first; + } else { + r = rtnl_rqueue_partial_make_room(rtnl); + if (r < 0) + return r; + + rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = first; + } + first = NULL; + + return 0; + } +} diff --git a/src/libsystemd/src/sd-netlink/netlink-types.c b/src/libsystemd/src/sd-netlink/netlink-types.c new file mode 100644 index 0000000000..3a4bac2ced --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-types.c @@ -0,0 +1,684 @@ +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen + + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "macro.h" +#include "missing.h" +#include "netlink-types.h" +#include "string-table.h" +#include "util.h" + +/* Maximum ARP IP target defined in kernel */ +#define BOND_MAX_ARP_TARGETS 16 + +typedef enum { + BOND_ARP_TARGETS_0, + BOND_ARP_TARGETS_1, + BOND_ARP_TARGETS_2, + BOND_ARP_TARGETS_3, + BOND_ARP_TARGETS_4, + BOND_ARP_TARGETS_5, + BOND_ARP_TARGETS_6, + BOND_ARP_TARGETS_7, + BOND_ARP_TARGETS_8, + BOND_ARP_TARGETS_9, + BOND_ARP_TARGETS_10, + BOND_ARP_TARGETS_11, + BOND_ARP_TARGETS_12, + BOND_ARP_TARGETS_13, + BOND_ARP_TARGETS_14, + BOND_ARP_TARGETS_MAX = BOND_MAX_ARP_TARGETS, +} BondArpTargets; + +struct NLType { + uint16_t type; + size_t size; + const NLTypeSystem *type_system; + const NLTypeSystemUnion *type_system_union; +}; + +struct NLTypeSystem { + uint16_t count; + const NLType *types; +}; + +static const NLTypeSystem rtnl_link_type_system; + +static const NLType empty_types[1] = { + /* fake array to avoid .types==NULL, which denotes invalid type-systems */ +}; + +static const NLTypeSystem empty_type_system = { + .count = 0, + .types = empty_types, +}; + +static const NLType rtnl_link_info_data_veth_types[] = { + [VETH_INFO_PEER] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, +}; + +static const NLType rtnl_link_info_data_ipvlan_types[] = { + [IFLA_IPVLAN_MODE] = { .type = NETLINK_TYPE_U16 }, +}; + +static const NLType rtnl_link_info_data_macvlan_types[] = { + [IFLA_MACVLAN_MODE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 }, +}; + +static const NLType rtnl_link_info_data_bridge_types[] = { + [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_AGEING_TIME] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_STP_STATE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_VLAN_FILTERING] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_ROOT_PORT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_ROOT_PATH_COST] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_TOPOLOGY_CHANGE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_TOPOLOGY_CHANGE_DETECTED] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_HELLO_TIMER] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_TCN_TIMER] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_TOPOLOGY_CHANGE_TIMER] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_GC_TIMER] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_GROUP_ADDR] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_FDB_FLUSH] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_MCAST_ROUTER] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_SNOOPING] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_QUERIER] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_MCAST_HASH_MAX] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_NF_CALL_IPTABLES] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NETLINK_TYPE_U16 }, +}; + +static const NLType rtnl_link_info_data_vlan_types[] = { + [IFLA_VLAN_ID] = { .type = NETLINK_TYPE_U16 }, +/* + [IFLA_VLAN_FLAGS] = { .len = sizeof(struct ifla_vlan_flags) }, + [IFLA_VLAN_EGRESS_QOS] = { .type = NETLINK_TYPE_NESTED }, + [IFLA_VLAN_INGRESS_QOS] = { .type = NETLINK_TYPE_NESTED }, +*/ + [IFLA_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 }, +}; + +static const NLType rtnl_link_info_data_vxlan_types[] = { + [IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 }, + [IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 }, + [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_U32}, + [IFLA_VXLAN_TTL] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_TOS] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_LEARNING] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_AGEING] = { .type = NETLINK_TYPE_U32 }, + [IFLA_VXLAN_LIMIT] = { .type = NETLINK_TYPE_U32 }, + [IFLA_VXLAN_PORT_RANGE] = { .type = NETLINK_TYPE_U32}, + [IFLA_VXLAN_PROXY] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_RSC] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_L2MISS] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_L3MISS] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_PORT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_VXLAN_GROUP6] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_VXLAN_LOCAL6] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_VXLAN_UDP_CSUM] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_REMCSUM_TX] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_REMCSUM_RX] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_GBP] = { .type = NETLINK_TYPE_FLAG }, + [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG }, +}; + +static const NLType rtnl_bond_arp_target_types[] = { + [BOND_ARP_TARGETS_0] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_1] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_2] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_3] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_4] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_5] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_6] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_7] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_8] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_9] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_10] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_11] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_12] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_13] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_14] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_MAX] = { .type = NETLINK_TYPE_U32 }, +}; + +static const NLTypeSystem rtnl_bond_arp_type_system = { + .count = ELEMENTSOF(rtnl_bond_arp_target_types), + .types = rtnl_bond_arp_target_types, +}; + +static const NLType rtnl_link_info_data_bond_types[] = { + [IFLA_BOND_MODE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_ACTIVE_SLAVE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_MIIMON] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_UPDELAY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_DOWNDELAY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_USE_CARRIER] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_ARP_INTERVAL] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_ARP_IP_TARGET] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bond_arp_type_system }, + [IFLA_BOND_ARP_VALIDATE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_ARP_ALL_TARGETS] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_PRIMARY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_PRIMARY_RESELECT] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_FAIL_OVER_MAC] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_XMIT_HASH_POLICY] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_RESEND_IGMP] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_NUM_PEER_NOTIF] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_ALL_SLAVES_ACTIVE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_MIN_LINKS] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_LP_INTERVAL] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_PACKETS_PER_SLAVE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_AD_LACP_RATE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_AD_SELECT] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_AD_INFO] = { .type = NETLINK_TYPE_NESTED }, +}; + +static const NLType rtnl_link_info_data_iptun_types[] = { + [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_TOS] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_PMTUDISC] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_FLAGS] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_6RD_PREFIX] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_IPTUN_6RD_RELAY_PREFIX] = { .type = NETLINK_TYPE_U32 }, + [IFLA_IPTUN_6RD_PREFIXLEN] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPTUN_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPTUN_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPTUN_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPTUN_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, +}; + +static const NLType rtnl_link_info_data_ipgre_types[] = { + [IFLA_GRE_LINK] = { .type = NETLINK_TYPE_U32 }, + [IFLA_GRE_IFLAGS] = { .type = NETLINK_TYPE_U16 }, + [IFLA_GRE_OFLAGS] = { .type = NETLINK_TYPE_U16 }, + [IFLA_GRE_IKEY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_GRE_OKEY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_GRE_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_GRE_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_GRE_TTL] = { .type = NETLINK_TYPE_U8 }, + [IFLA_GRE_TOS] = { .type = NETLINK_TYPE_U8 }, + [IFLA_GRE_PMTUDISC] = { .type = NETLINK_TYPE_U8 }, + [IFLA_GRE_FLOWINFO] = { .type = NETLINK_TYPE_U32 }, + [IFLA_GRE_FLAGS] = { .type = NETLINK_TYPE_U32 }, + [IFLA_GRE_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 }, + [IFLA_GRE_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 }, + [IFLA_GRE_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_GRE_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, +}; + +static const NLType rtnl_link_info_data_ipvti_types[] = { + [IFLA_VTI_LINK] = { .type = NETLINK_TYPE_U32 }, + [IFLA_VTI_IKEY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_VTI_OKEY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_VTI_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_VTI_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, +}; + +static const NLType rtnl_link_info_data_ip6tnl_types[] = { + [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_FLAGS] = { .type = NETLINK_TYPE_U32 }, + [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_FLOWINFO] = { .type = NETLINK_TYPE_U32 }, +}; + +/* these strings must match the .kind entries in the kernel */ +static const char* const nl_union_link_info_data_table[] = { + [NL_UNION_LINK_INFO_DATA_BOND] = "bond", + [NL_UNION_LINK_INFO_DATA_BRIDGE] = "bridge", + [NL_UNION_LINK_INFO_DATA_VLAN] = "vlan", + [NL_UNION_LINK_INFO_DATA_VETH] = "veth", + [NL_UNION_LINK_INFO_DATA_DUMMY] = "dummy", + [NL_UNION_LINK_INFO_DATA_MACVLAN] = "macvlan", + [NL_UNION_LINK_INFO_DATA_MACVTAP] = "macvtap", + [NL_UNION_LINK_INFO_DATA_IPVLAN] = "ipvlan", + [NL_UNION_LINK_INFO_DATA_VXLAN] = "vxlan", + [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = "ipip", + [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = "gre", + [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = "gretap", + [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = "ip6gre", + [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = "ip6gretap", + [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = "sit", + [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = "vti", + [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = "vti6", + [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = "ip6tnl", +}; + +DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData); + +static const NLTypeSystem rtnl_link_info_data_type_systems[] = { + [NL_UNION_LINK_INFO_DATA_BOND] = { .count = ELEMENTSOF(rtnl_link_info_data_bond_types), + .types = rtnl_link_info_data_bond_types }, + [NL_UNION_LINK_INFO_DATA_BRIDGE] = { .count = ELEMENTSOF(rtnl_link_info_data_bridge_types), + .types = rtnl_link_info_data_bridge_types }, + [NL_UNION_LINK_INFO_DATA_VLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vlan_types), + .types = rtnl_link_info_data_vlan_types }, + [NL_UNION_LINK_INFO_DATA_VETH] = { .count = ELEMENTSOF(rtnl_link_info_data_veth_types), + .types = rtnl_link_info_data_veth_types }, + [NL_UNION_LINK_INFO_DATA_MACVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), + .types = rtnl_link_info_data_macvlan_types }, + [NL_UNION_LINK_INFO_DATA_MACVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), + .types = rtnl_link_info_data_macvlan_types }, + [NL_UNION_LINK_INFO_DATA_IPVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types), + .types = rtnl_link_info_data_ipvlan_types }, + [NL_UNION_LINK_INFO_DATA_VXLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types), + .types = rtnl_link_info_data_vxlan_types }, + [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), + .types = rtnl_link_info_data_iptun_types }, + [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), + .types = rtnl_link_info_data_ipgre_types }, + [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), + .types = rtnl_link_info_data_ipgre_types }, + [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), + .types = rtnl_link_info_data_ipgre_types }, + [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), + .types = rtnl_link_info_data_ipgre_types }, + [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), + .types = rtnl_link_info_data_iptun_types }, + [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types), + .types = rtnl_link_info_data_ipvti_types }, + [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types), + .types = rtnl_link_info_data_ipvti_types }, + [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ip6tnl_types), + .types = rtnl_link_info_data_ip6tnl_types }, + +}; + +static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = { + .num = _NL_UNION_LINK_INFO_DATA_MAX, + .lookup = nl_union_link_info_data_from_string, + .type_systems = rtnl_link_info_data_type_systems, + .match_type = NL_MATCH_SIBLING, + .match = IFLA_INFO_KIND, +}; + +static const NLType rtnl_link_info_types[] = { + [IFLA_INFO_KIND] = { .type = NETLINK_TYPE_STRING }, + [IFLA_INFO_DATA] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_link_info_data_type_system_union}, +/* + [IFLA_INFO_XSTATS], + [IFLA_INFO_SLAVE_KIND] = { .type = NETLINK_TYPE_STRING }, + [IFLA_INFO_SLAVE_DATA] = { .type = NETLINK_TYPE_NESTED }, +*/ +}; + +static const NLTypeSystem rtnl_link_info_type_system = { + .count = ELEMENTSOF(rtnl_link_info_types), + .types = rtnl_link_info_types, +}; + +static const struct NLType rtnl_prot_info_bridge_port_types[] = { + [IFLA_BRPORT_STATE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_COST] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BRPORT_PRIORITY] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BRPORT_MODE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_GUARD] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_PROTECT] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_FAST_LEAVE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_LEARNING] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_PROXYARP] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 }, +}; + +static const NLTypeSystem rtnl_prot_info_type_systems[] = { + [AF_BRIDGE] = { .count = ELEMENTSOF(rtnl_prot_info_bridge_port_types), + .types = rtnl_prot_info_bridge_port_types }, +}; + +static const NLTypeSystemUnion rtnl_prot_info_type_system_union = { + .num = AF_MAX, + .type_systems = rtnl_prot_info_type_systems, + .match_type = NL_MATCH_PROTOCOL, +}; + +static const struct NLType rtnl_af_spec_inet6_types[] = { + [IFLA_INET6_FLAGS] = { .type = NETLINK_TYPE_U32 }, +/* + IFLA_INET6_CONF, + IFLA_INET6_STATS, + IFLA_INET6_MCAST, + IFLA_INET6_CACHEINFO, + IFLA_INET6_ICMP6STATS, +*/ + [IFLA_INET6_TOKEN] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_INET6_ADDR_GEN_MODE] = { .type = NETLINK_TYPE_U8 }, +}; + +static const NLTypeSystem rtnl_af_spec_inet6_type_system = { + .count = ELEMENTSOF(rtnl_af_spec_inet6_types), + .types = rtnl_af_spec_inet6_types, +}; + +static const NLType rtnl_af_spec_types[] = { + [AF_INET6] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_inet6_type_system }, +}; + +static const NLTypeSystem rtnl_af_spec_type_system = { + .count = ELEMENTSOF(rtnl_af_spec_types), + .types = rtnl_af_spec_types, +}; + +static const NLType rtnl_link_types[] = { + [IFLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR }, + [IFLA_BROADCAST] = { .type = NETLINK_TYPE_ETHER_ADDR }, + [IFLA_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 }, + [IFLA_MTU] = { .type = NETLINK_TYPE_U32 }, + [IFLA_LINK] = { .type = NETLINK_TYPE_U32 }, +/* + [IFLA_QDISC], + [IFLA_STATS], + [IFLA_COST], + [IFLA_PRIORITY], +*/ + [IFLA_MASTER] = { .type = NETLINK_TYPE_U32 }, +/* + [IFLA_WIRELESS], +*/ + [IFLA_PROTINFO] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_prot_info_type_system_union }, + [IFLA_TXQLEN] = { .type = NETLINK_TYPE_U32 }, +/* + [IFLA_MAP] = { .len = sizeof(struct rtnl_link_ifmap) }, +*/ + [IFLA_WEIGHT] = { .type = NETLINK_TYPE_U32 }, + [IFLA_OPERSTATE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_LINKMODE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_LINKINFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system }, + [IFLA_NET_NS_PID] = { .type = NETLINK_TYPE_U32 }, + [IFLA_IFALIAS] = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 }, +/* + [IFLA_NUM_VF], + [IFLA_VFINFO_LIST] = {. type = NETLINK_TYPE_NESTED, }, + [IFLA_STATS64], + [IFLA_VF_PORTS] = { .type = NETLINK_TYPE_NESTED }, + [IFLA_PORT_SELF] = { .type = NETLINK_TYPE_NESTED }, +*/ + [IFLA_AF_SPEC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_type_system }, +/* + [IFLA_VF_PORTS], + [IFLA_PORT_SELF], + [IFLA_AF_SPEC], +*/ + [IFLA_GROUP] = { .type = NETLINK_TYPE_U32 }, + [IFLA_NET_NS_FD] = { .type = NETLINK_TYPE_U32 }, + [IFLA_EXT_MASK] = { .type = NETLINK_TYPE_U32 }, + [IFLA_PROMISCUITY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_NUM_TX_QUEUES] = { .type = NETLINK_TYPE_U32 }, + [IFLA_NUM_RX_QUEUES] = { .type = NETLINK_TYPE_U32 }, + [IFLA_CARRIER] = { .type = NETLINK_TYPE_U8 }, +/* + [IFLA_PHYS_PORT_ID] = { .type = NETLINK_TYPE_BINARY, .len = MAX_PHYS_PORT_ID_LEN }, +*/ +}; + +static const NLTypeSystem rtnl_link_type_system = { + .count = ELEMENTSOF(rtnl_link_types), + .types = rtnl_link_types, +}; + +/* IFA_FLAGS was defined in kernel 3.14, but we still support older + * kernels where IFA_MAX is lower. */ +static const NLType rtnl_address_types[] = { + [IFA_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFA_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFA_LABEL] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 }, + [IFA_BROADCAST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ + [IFA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct ifa_cacheinfo) }, +/* + [IFA_ANYCAST], + [IFA_MULTICAST], +*/ + [IFA_FLAGS] = { .type = NETLINK_TYPE_U32 }, +}; + +static const NLTypeSystem rtnl_address_type_system = { + .count = ELEMENTSOF(rtnl_address_types), + .types = rtnl_address_types, +}; + +static const NLType rtnl_route_types[] = { + [RTA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ + [RTA_SRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ + [RTA_IIF] = { .type = NETLINK_TYPE_U32 }, + [RTA_OIF] = { .type = NETLINK_TYPE_U32 }, + [RTA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR }, + [RTA_PRIORITY] = { .type = NETLINK_TYPE_U32 }, + [RTA_PREFSRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ +/* + [RTA_METRICS] = { .type = NETLINK_TYPE_NESTED }, + [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, +*/ + [RTA_FLOW] = { .type = NETLINK_TYPE_U32 }, /* 6? */ +/* + RTA_CACHEINFO, + RTA_TABLE, + RTA_MARK, + RTA_MFC_STATS, + RTA_VIA, + RTA_NEWDST, +*/ + [RTA_PREF] = { .type = NETLINK_TYPE_U8 }, + +}; + +static const NLTypeSystem rtnl_route_type_system = { + .count = ELEMENTSOF(rtnl_route_types), + .types = rtnl_route_types, +}; + +static const NLType rtnl_neigh_types[] = { + [NDA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, + [NDA_LLADDR] = { .type = NETLINK_TYPE_ETHER_ADDR }, + [NDA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) }, + [NDA_PROBES] = { .type = NETLINK_TYPE_U32 }, + [NDA_VLAN] = { .type = NETLINK_TYPE_U16 }, + [NDA_PORT] = { .type = NETLINK_TYPE_U16 }, + [NDA_VNI] = { .type = NETLINK_TYPE_U32 }, + [NDA_IFINDEX] = { .type = NETLINK_TYPE_U32 }, +}; + +static const NLTypeSystem rtnl_neigh_type_system = { + .count = ELEMENTSOF(rtnl_neigh_types), + .types = rtnl_neigh_types, +}; + +static const NLType rtnl_types[] = { + [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 }, + [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) }, + [RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_DELLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_GETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_SETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_NEWADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, + [RTM_DELADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, + [RTM_GETADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, + [RTM_NEWROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, + [RTM_DELROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, + [RTM_GETROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, + [RTM_NEWNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, + [RTM_DELNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, + [RTM_GETNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, +}; + +const NLTypeSystem type_system_root = { + .count = ELEMENTSOF(rtnl_types), + .types = rtnl_types, +}; + +uint16_t type_get_type(const NLType *type) { + assert(type); + return type->type; +} + +size_t type_get_size(const NLType *type) { + assert(type); + return type->size; +} + +void type_get_type_system(const NLType *nl_type, const NLTypeSystem **ret) { + assert(nl_type); + assert(ret); + assert(nl_type->type == NETLINK_TYPE_NESTED); + assert(nl_type->type_system); + + *ret = nl_type->type_system; +} + +void type_get_type_system_union(const NLType *nl_type, const NLTypeSystemUnion **ret) { + assert(nl_type); + assert(ret); + assert(nl_type->type == NETLINK_TYPE_UNION); + assert(nl_type->type_system_union); + + *ret = nl_type->type_system_union; +} + +uint16_t type_system_get_count(const NLTypeSystem *type_system) { + assert(type_system); + return type_system->count; +} + +int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type) { + const NLType *nl_type; + + assert(ret); + assert(type_system); + assert(type_system->types); + + if (type >= type_system->count) + return -EOPNOTSUPP; + + nl_type = &type_system->types[type]; + + if (nl_type->type == NETLINK_TYPE_UNSPEC) + return -EOPNOTSUPP; + + *ret = nl_type; + + return 0; +} + +int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type) { + const NLType *nl_type; + int r; + + assert(ret); + + r = type_system_get_type(type_system, &nl_type, type); + if (r < 0) + return r; + + type_get_type_system(nl_type, ret); + return 0; +} + +int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type) { + const NLType *nl_type; + int r; + + assert(ret); + + r = type_system_get_type(type_system, &nl_type, type); + if (r < 0) + return r; + + type_get_type_system_union(nl_type, ret); + return 0; +} + +int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key) { + int type; + + assert(type_system_union); + assert(type_system_union->match_type == NL_MATCH_SIBLING); + assert(type_system_union->lookup); + assert(type_system_union->type_systems); + assert(ret); + assert(key); + + type = type_system_union->lookup(key); + if (type < 0) + return -EOPNOTSUPP; + + assert(type < type_system_union->num); + + *ret = &type_system_union->type_systems[type]; + + return 0; +} + +int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol) { + const NLTypeSystem *type_system; + + assert(type_system_union); + assert(type_system_union->type_systems); + assert(type_system_union->match_type == NL_MATCH_PROTOCOL); + assert(ret); + + if (protocol >= type_system_union->num) + return -EOPNOTSUPP; + + type_system = &type_system_union->type_systems[protocol]; + if (!type_system->types) + return -EOPNOTSUPP; + + *ret = type_system; + + return 0; +} diff --git a/src/libsystemd/src/sd-netlink/netlink-types.h b/src/libsystemd/src/sd-netlink/netlink-types.h new file mode 100644 index 0000000000..ecb20bfcdc --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-types.h @@ -0,0 +1,94 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen + + 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 . +***/ + +#include "macro.h" + +enum { + NETLINK_TYPE_UNSPEC, + NETLINK_TYPE_U8, /* NLA_U8 */ + NETLINK_TYPE_U16, /* NLA_U16 */ + NETLINK_TYPE_U32, /* NLA_U32 */ + NETLINK_TYPE_U64, /* NLA_U64 */ + NETLINK_TYPE_STRING, /* NLA_STRING */ + NETLINK_TYPE_FLAG, /* NLA_FLAG */ + NETLINK_TYPE_IN_ADDR, + NETLINK_TYPE_ETHER_ADDR, + NETLINK_TYPE_CACHE_INFO, + NETLINK_TYPE_NESTED, /* NLA_NESTED */ + NETLINK_TYPE_UNION, +}; + +typedef enum NLMatchType { + NL_MATCH_SIBLING, + NL_MATCH_PROTOCOL, +} NLMatchType; + +typedef struct NLTypeSystemUnion NLTypeSystemUnion; +typedef struct NLTypeSystem NLTypeSystem; +typedef struct NLType NLType; + +struct NLTypeSystemUnion { + int num; + NLMatchType match_type; + uint16_t match; + int (*lookup)(const char *); + const NLTypeSystem *type_systems; +}; + +extern const NLTypeSystem type_system_root; + +uint16_t type_get_type(const NLType *type); +size_t type_get_size(const NLType *type); +void type_get_type_system(const NLType *type, const NLTypeSystem **ret); +void type_get_type_system_union(const NLType *type, const NLTypeSystemUnion **ret); + +uint16_t type_system_get_count(const NLTypeSystem *type_system); +int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type); +int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type); +int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type); +int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key); +int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol); + +typedef enum NLUnionLinkInfoData { + NL_UNION_LINK_INFO_DATA_BOND, + NL_UNION_LINK_INFO_DATA_BRIDGE, + NL_UNION_LINK_INFO_DATA_VLAN, + NL_UNION_LINK_INFO_DATA_VETH, + NL_UNION_LINK_INFO_DATA_DUMMY, + NL_UNION_LINK_INFO_DATA_MACVLAN, + NL_UNION_LINK_INFO_DATA_MACVTAP, + NL_UNION_LINK_INFO_DATA_IPVLAN, + NL_UNION_LINK_INFO_DATA_VXLAN, + NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL, + NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL, + NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL, + NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL, + NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL, + NL_UNION_LINK_INFO_DATA_SIT_TUNNEL, + NL_UNION_LINK_INFO_DATA_VTI_TUNNEL, + NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL, + NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL, + _NL_UNION_LINK_INFO_DATA_MAX, + _NL_UNION_LINK_INFO_DATA_INVALID = -1 +} NLUnionLinkInfoData; + +const char *nl_union_link_info_data_to_string(NLUnionLinkInfoData p) _const_; +NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_; diff --git a/src/libsystemd/src/sd-netlink/netlink-util.c b/src/libsystemd/src/sd-netlink/netlink-util.c new file mode 100644 index 0000000000..828ae7db7f --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-util.c @@ -0,0 +1,170 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 Tom Gundersen + + 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 . +***/ + +#include + +#include "netlink-internal.h" +#include "netlink-util.h" + +int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; + int r; + + assert(rtnl); + assert(ifindex > 0); + assert(name); + + if (!*rtnl) { + r = sd_netlink_open(rtnl); + if (r < 0) + return r; + } + + r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); + if (r < 0) + return r; + + r = sd_netlink_message_append_string(message, IFLA_IFNAME, name); + if (r < 0) + return r; + + r = sd_netlink_call(*rtnl, message, 0, NULL); + if (r < 0) + return r; + + return 0; +} + +int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, + const struct ether_addr *mac, unsigned mtu) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; + int r; + + assert(rtnl); + assert(ifindex > 0); + + if (!alias && !mac && mtu == 0) + return 0; + + if (!*rtnl) { + r = sd_netlink_open(rtnl); + if (r < 0) + return r; + } + + r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); + if (r < 0) + return r; + + if (alias) { + r = sd_netlink_message_append_string(message, IFLA_IFALIAS, alias); + if (r < 0) + return r; + } + + if (mac) { + r = sd_netlink_message_append_ether_addr(message, IFLA_ADDRESS, mac); + if (r < 0) + return r; + } + + if (mtu > 0) { + r = sd_netlink_message_append_u32(message, IFLA_MTU, mtu); + if (r < 0) + return r; + } + + r = sd_netlink_call(*rtnl, message, 0, NULL); + if (r < 0) + return r; + + return 0; +} + +int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret) { + struct nlmsgerr *err; + int r; + + assert(error <= 0); + + r = message_new(NULL, ret, NLMSG_ERROR); + if (r < 0) + return r; + + (*ret)->hdr->nlmsg_seq = serial; + + err = NLMSG_DATA((*ret)->hdr); + + err->error = error; + + return 0; +} + +bool rtnl_message_type_is_neigh(uint16_t type) { + switch (type) { + case RTM_NEWNEIGH: + case RTM_GETNEIGH: + case RTM_DELNEIGH: + return true; + default: + return false; + } +} + +bool rtnl_message_type_is_route(uint16_t type) { + switch (type) { + case RTM_NEWROUTE: + case RTM_GETROUTE: + case RTM_DELROUTE: + return true; + default: + return false; + } +} + +bool rtnl_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 rtnl_message_type_is_addr(uint16_t type) { + switch (type) { + case RTM_NEWADDR: + case RTM_GETADDR: + case RTM_DELADDR: + return true; + default: + return false; + } +} + +int rtnl_log_parse_error(int r) { + return log_error_errno(r, "Failed to parse netlink message: %m"); +} + +int rtnl_log_create_error(int r) { + return log_error_errno(r, "Failed to create netlink message: %m"); +} diff --git a/src/libsystemd/src/sd-netlink/netlink-util.h b/src/libsystemd/src/sd-netlink/netlink-util.h new file mode 100644 index 0000000000..e8f932549f --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-util.h @@ -0,0 +1,39 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 Tom Gundersen + + 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 . +***/ + +#include + +#include "util.h" + +int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret); +uint32_t rtnl_message_get_serial(sd_netlink_message *m); +void rtnl_message_seal(sd_netlink_message *m); + +bool rtnl_message_type_is_link(uint16_t type); +bool rtnl_message_type_is_addr(uint16_t type); +bool rtnl_message_type_is_route(uint16_t type); +bool rtnl_message_type_is_neigh(uint16_t type); + +int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name); +int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, unsigned mtu); + +int rtnl_log_parse_error(int r); +int rtnl_log_create_error(int r); diff --git a/src/libsystemd/src/sd-netlink/rtnl-message.c b/src/libsystemd/src/sd-netlink/rtnl-message.c new file mode 100644 index 0000000000..f6482a6157 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/rtnl-message.c @@ -0,0 +1,702 @@ +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include +#include +#include + +#include + +#include "formats-util.h" +#include "missing.h" +#include "netlink-internal.h" +#include "netlink-types.h" +#include "netlink-util.h" +#include "refcnt.h" +#include "socket-util.h" +#include "util.h" + +int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + if ((rtm->rtm_family == AF_INET && prefixlen > 32) || + (rtm->rtm_family == AF_INET6 && prefixlen > 128)) + return -ERANGE; + + rtm->rtm_dst_len = prefixlen; + + return 0; +} + +int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + if ((rtm->rtm_family == AF_INET && prefixlen > 32) || + (rtm->rtm_family == AF_INET6 && prefixlen > 128)) + return -ERANGE; + + rtm->rtm_src_len = prefixlen; + + return 0; +} + +int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_scope = scope; + + return 0; +} + +int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_flags = flags; + + return 0; +} + +int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(flags, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *flags = rtm->rtm_flags; + + return 0; +} + +int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_table = table; + + return 0; +} + +int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(family, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *family = rtm->rtm_family; + + return 0; +} + +int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_family = family; + + return 0; +} + +int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(protocol, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *protocol = rtm->rtm_protocol; + + return 0; +} + +int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(scope, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *scope = rtm->rtm_scope; + + return 0; +} + +int sd_rtnl_message_route_get_tos(sd_netlink_message *m, unsigned char *tos) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(tos, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *tos = rtm->rtm_tos; + + return 0; +} + +int sd_rtnl_message_route_get_table(sd_netlink_message *m, unsigned char *table) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(table, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *table = rtm->rtm_table; + + return 0; +} + +int sd_rtnl_message_route_get_dst_prefixlen(sd_netlink_message *m, unsigned char *dst_len) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(dst_len, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *dst_len = rtm->rtm_dst_len; + + return 0; +} + +int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(src_len, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *src_len = rtm->rtm_src_len; + + return 0; +} + +int sd_rtnl_message_new_route(sd_netlink *rtnl, sd_netlink_message **ret, + uint16_t nlmsg_type, int rtm_family, + unsigned char rtm_protocol) { + struct rtmsg *rtm; + int r; + + assert_return(rtnl_message_type_is_route(nlmsg_type), -EINVAL); + assert_return((nlmsg_type == RTM_GETROUTE && rtm_family == AF_UNSPEC) || + rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(rtnl, ret, nlmsg_type); + if (r < 0) + return r; + + if (nlmsg_type == RTM_NEWROUTE) + (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND; + + rtm = NLMSG_DATA((*ret)->hdr); + + 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 = rtm_protocol; + + return 0; +} + +int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + ndm->ndm_flags |= flags; + + return 0; +} + +int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + ndm->ndm_state |= state; + + return 0; +} + +int sd_rtnl_message_neigh_get_flags(sd_netlink_message *m, uint8_t *flags) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + *flags = ndm->ndm_flags; + + return 0; +} + +int sd_rtnl_message_neigh_get_state(sd_netlink_message *m, uint16_t *state) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + *state = ndm->ndm_state; + + return 0; +} + +int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + assert_return(family, -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + + *family = ndm->ndm_family; + + return 0; +} + +int sd_rtnl_message_neigh_get_ifindex(sd_netlink_message *m, int *index) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + assert_return(index, -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + + *index = ndm->ndm_ifindex; + + return 0; +} + +int sd_rtnl_message_new_neigh(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int index, int ndm_family) { + struct ndmsg *ndm; + int r; + + assert_return(rtnl_message_type_is_neigh(nlmsg_type), -EINVAL); + assert_return(ndm_family == AF_INET || + ndm_family == AF_INET6 || + ndm_family == PF_BRIDGE, -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(rtnl, ret, nlmsg_type); + if (r < 0) + return r; + + if (nlmsg_type == RTM_NEWNEIGH) + (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND; + + ndm = NLMSG_DATA((*ret)->hdr); + + ndm->ndm_family = ndm_family; + ndm->ndm_ifindex = index; + + return 0; +} + +int sd_rtnl_message_link_set_flags(sd_netlink_message *m, unsigned flags, unsigned change) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + assert_return(change, -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + ifi->ifi_flags = flags; + ifi->ifi_change = change; + + return 0; +} + +int sd_rtnl_message_link_set_type(sd_netlink_message *m, unsigned type) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + ifi->ifi_type = type; + + return 0; +} + +int sd_rtnl_message_link_set_family(sd_netlink_message *m, unsigned family) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + ifi->ifi_family = family; + + return 0; +} + +int sd_rtnl_message_new_link(sd_netlink *rtnl, sd_netlink_message **ret, + uint16_t nlmsg_type, int index) { + struct ifinfomsg *ifi; + int r; + + assert_return(rtnl_message_type_is_link(nlmsg_type), -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(rtnl, ret, nlmsg_type); + if (r < 0) + return r; + + if (nlmsg_type == RTM_NEWLINK) + (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; + + ifi = NLMSG_DATA((*ret)->hdr); + + ifi->ifi_family = AF_UNSPEC; + ifi->ifi_index = index; + + return 0; +} + +int sd_rtnl_message_addr_set_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + if ((ifa->ifa_family == AF_INET && prefixlen > 32) || + (ifa->ifa_family == AF_INET6 && prefixlen > 128)) + return -ERANGE; + + ifa->ifa_prefixlen = prefixlen; + + return 0; +} + +int sd_rtnl_message_addr_set_flags(sd_netlink_message *m, unsigned char flags) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + ifa->ifa_flags = flags; + + return 0; +} + +int sd_rtnl_message_addr_set_scope(sd_netlink_message *m, unsigned char scope) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + ifa->ifa_scope = scope; + + return 0; +} + +int sd_rtnl_message_addr_get_family(sd_netlink_message *m, int *family) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(family, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *family = ifa->ifa_family; + + return 0; +} + +int sd_rtnl_message_addr_get_prefixlen(sd_netlink_message *m, unsigned char *prefixlen) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(prefixlen, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *prefixlen = ifa->ifa_prefixlen; + + return 0; +} + +int sd_rtnl_message_addr_get_scope(sd_netlink_message *m, unsigned char *scope) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(scope, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *scope = ifa->ifa_scope; + + return 0; +} + +int sd_rtnl_message_addr_get_flags(sd_netlink_message *m, unsigned char *flags) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(flags, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *flags = ifa->ifa_flags; + + return 0; +} + +int sd_rtnl_message_addr_get_ifindex(sd_netlink_message *m, int *ifindex) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(ifindex, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *ifindex = ifa->ifa_index; + + return 0; +} + +int sd_rtnl_message_new_addr(sd_netlink *rtnl, sd_netlink_message **ret, + uint16_t nlmsg_type, int index, + int family) { + struct ifaddrmsg *ifa; + int r; + + assert_return(rtnl_message_type_is_addr(nlmsg_type), -EINVAL); + assert_return((nlmsg_type == RTM_GETADDR && index == 0) || + index > 0, -EINVAL); + assert_return((nlmsg_type == RTM_GETADDR && family == AF_UNSPEC) || + family == AF_INET || family == AF_INET6, -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(rtnl, ret, nlmsg_type); + if (r < 0) + return r; + + if (nlmsg_type == RTM_GETADDR) + (*ret)->hdr->nlmsg_flags |= NLM_F_DUMP; + + ifa = NLMSG_DATA((*ret)->hdr); + + ifa->ifa_index = index; + ifa->ifa_family = family; + if (family == AF_INET) + ifa->ifa_prefixlen = 32; + else if (family == AF_INET6) + ifa->ifa_prefixlen = 128; + + return 0; +} + +int sd_rtnl_message_new_addr_update(sd_netlink *rtnl, sd_netlink_message **ret, + int index, int family) { + int r; + + r = sd_rtnl_message_new_addr(rtnl, ret, RTM_NEWADDR, index, family); + if (r < 0) + return r; + + (*ret)->hdr->nlmsg_flags |= NLM_F_REPLACE; + + return 0; +} + +int sd_rtnl_message_link_get_ifindex(sd_netlink_message *m, int *ifindex) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_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_netlink_message *m, unsigned *flags) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + assert_return(flags, -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + *flags = ifi->ifi_flags; + + return 0; +} + +int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned short *type) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + assert_return(type, -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + *type = ifi->ifi_type; + + return 0; +} + +int sd_rtnl_message_get_family(sd_netlink_message *m, int *family) { + assert_return(m, -EINVAL); + assert_return(family, -EINVAL); + + assert(m->hdr); + + if (rtnl_message_type_is_link(m->hdr->nlmsg_type)) { + struct ifinfomsg *ifi; + + ifi = NLMSG_DATA(m->hdr); + + *family = ifi->ifi_family; + + return 0; + } else if (rtnl_message_type_is_route(m->hdr->nlmsg_type)) { + struct rtmsg *rtm; + + rtm = NLMSG_DATA(m->hdr); + + *family = rtm->rtm_family; + + return 0; + } else if (rtnl_message_type_is_neigh(m->hdr->nlmsg_type)) { + struct ndmsg *ndm; + + ndm = NLMSG_DATA(m->hdr); + + *family = ndm->ndm_family; + + return 0; + } else if (rtnl_message_type_is_addr(m->hdr->nlmsg_type)) { + struct ifaddrmsg *ifa; + + ifa = NLMSG_DATA(m->hdr); + + *family = ifa->ifa_family; + + return 0; + } + + return -EOPNOTSUPP; +} diff --git a/src/libsystemd/src/sd-netlink/sd-netlink.c b/src/libsystemd/src/sd-netlink/sd-netlink.c new file mode 100644 index 0000000000..3c7488463c --- /dev/null +++ b/src/libsystemd/src/sd-netlink/sd-netlink.c @@ -0,0 +1,954 @@ +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include +#include + +#include + +#include "alloc-util.h" +#include "fd-util.h" +#include "hashmap.h" +#include "macro.h" +#include "missing.h" +#include "netlink-internal.h" +#include "netlink-util.h" +#include "socket-util.h" +#include "util.h" + +static int sd_netlink_new(sd_netlink **ret) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + + assert_return(ret, -EINVAL); + + rtnl = new0(sd_netlink, 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 the read buffer has at least space for + * a message header */ + if (!greedy_realloc((void**)&rtnl->rbuffer, &rtnl->rbuffer_allocated, + sizeof(struct nlmsghdr), sizeof(uint8_t))) + return -ENOMEM; + + /* Change notification responses have sequence 0, so we must + * start our request sequence numbers at 1, or we may confuse our + * responses with notifications from the kernel */ + rtnl->serial = 1; + + *ret = rtnl; + rtnl = NULL; + + return 0; +} + +int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + socklen_t addrlen; + int r; + + assert_return(ret, -EINVAL); + + r = sd_netlink_new(&rtnl); + if (r < 0) + return r; + + addrlen = sizeof(rtnl->sockaddr); + + r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen); + if (r < 0) + return -errno; + + if (rtnl->sockaddr.nl.nl_family != AF_NETLINK) + return -EINVAL; + + rtnl->fd = fd; + + *ret = rtnl; + rtnl = NULL; + + return 0; +} + +static bool rtnl_pid_changed(sd_netlink *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_netlink_open_fd(sd_netlink **ret, int fd) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + int r; + + assert_return(ret, -EINVAL); + assert_return(fd >= 0, -EBADF); + + r = sd_netlink_new(&rtnl); + if (r < 0) + return r; + + rtnl->fd = fd; + + r = socket_bind(rtnl); + if (r < 0) { + rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */ + return r; + } + + *ret = rtnl; + rtnl = NULL; + + return 0; +} + +int sd_netlink_open(sd_netlink **ret) { + _cleanup_close_ int fd = -1; + int r; + + fd = socket_open(NETLINK_ROUTE); + if (fd < 0) + return fd; + + r = sd_netlink_open_fd(ret, fd); + if (r < 0) + return r; + + fd = -1; + + return 0; +} + +int sd_netlink_inc_rcvbuf(const sd_netlink *const rtnl, const int size) { + return fd_inc_rcvbuf(rtnl->fd, size); +} + +sd_netlink *sd_netlink_ref(sd_netlink *rtnl) { + assert_return(rtnl, NULL); + assert_return(!rtnl_pid_changed(rtnl), NULL); + + if (rtnl) + assert_se(REFCNT_INC(rtnl->n_ref) >= 2); + + return rtnl; +} + +sd_netlink *sd_netlink_unref(sd_netlink *rtnl) { + if (!rtnl) + return NULL; + + assert_return(!rtnl_pid_changed(rtnl), NULL); + + if (REFCNT_DEC(rtnl->n_ref) == 0) { + struct match_callback *f; + unsigned i; + + for (i = 0; i < rtnl->rqueue_size; i++) + sd_netlink_message_unref(rtnl->rqueue[i]); + free(rtnl->rqueue); + + for (i = 0; i < rtnl->rqueue_partial_size; i++) + sd_netlink_message_unref(rtnl->rqueue_partial[i]); + free(rtnl->rqueue_partial); + + free(rtnl->rbuffer); + + hashmap_free_free(rtnl->reply_callbacks); + prioq_free(rtnl->reply_callbacks_prioq); + + sd_event_source_unref(rtnl->io_event_source); + sd_event_source_unref(rtnl->time_event_source); + sd_event_unref(rtnl->event); + + while ((f = rtnl->match_callbacks)) { + sd_netlink_remove_match(rtnl, f->type, f->callback, f->userdata); + } + + hashmap_free(rtnl->broadcast_group_refs); + + safe_close(rtnl->fd); + free(rtnl); + } + + return NULL; +} + +static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) { + assert(rtnl); + assert(!rtnl_pid_changed(rtnl)); + assert(m); + assert(m->hdr); + + /* don't use seq == 0, as that is used for broadcasts, so we + would get confused by replies to such messages */ + m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++; + + rtnl_message_seal(m); + + return; +} + +int sd_netlink_send(sd_netlink *nl, + sd_netlink_message *message, + uint32_t *serial) { + int r; + + assert_return(nl, -EINVAL); + assert_return(!rtnl_pid_changed(nl), -ECHILD); + assert_return(message, -EINVAL); + assert_return(!message->sealed, -EPERM); + + rtnl_seal_message(nl, message); + + r = socket_write_message(nl, message); + if (r < 0) + return r; + + if (serial) + *serial = rtnl_message_get_serial(message); + + return 1; +} + +int rtnl_rqueue_make_room(sd_netlink *rtnl) { + assert(rtnl); + + if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX) { + log_debug("rtnl: exhausted the read queue size (%d)", RTNL_RQUEUE_MAX); + return -ENOBUFS; + } + + if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_allocated, rtnl->rqueue_size + 1)) + return -ENOMEM; + + return 0; +} + +int rtnl_rqueue_partial_make_room(sd_netlink *rtnl) { + assert(rtnl); + + if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX) { + log_debug("rtnl: exhausted the partial read queue size (%d)", RTNL_RQUEUE_MAX); + return -ENOBUFS; + } + + if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_allocated, + rtnl->rqueue_partial_size + 1)) + return -ENOMEM; + + return 0; +} + +static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) { + int r; + + assert(rtnl); + assert(message); + + if (rtnl->rqueue_size <= 0) { + /* Try to read a new message */ + r = socket_read_message(rtnl); + if (r <= 0) + return r; + } + + /* Dispatch a queued message */ + *message = rtnl->rqueue[0]; + rtnl->rqueue_size--; + memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_netlink_message*) * rtnl->rqueue_size); + + return 1; +} + +static int process_timeout(sd_netlink *rtnl) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_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 = rtnl_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); + if (r < 0) + log_debug_errno(r, "sd-netlink: timedout callback failed: %m"); + + free(c); + + return 1; +} + +static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) { + _cleanup_free_ struct reply_callback *c = NULL; + uint64_t serial; + uint16_t type; + int r; + + assert(rtnl); + assert(m); + + serial = rtnl_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 = sd_netlink_message_get_type(m, &type); + if (r < 0) + return 0; + + if (type == NLMSG_DONE) + m = NULL; + + r = c->callback(rtnl, m, c->userdata); + if (r < 0) + log_debug_errno(r, "sd-netlink: callback failed: %m"); + + return 1; +} + +static int process_match(sd_netlink *rtnl, sd_netlink_message *m) { + struct match_callback *c; + uint16_t type; + int r; + + assert(rtnl); + assert(m); + + r = sd_netlink_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) { + if (r < 0) + log_debug_errno(r, "sd-netlink: match callback failed: %m"); + + break; + } + } + } + + return 1; +} + +static int process_running(sd_netlink *rtnl, sd_netlink_message **ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + assert(rtnl); + + r = process_timeout(rtnl); + if (r != 0) + goto null_message; + + r = dispatch_rqueue(rtnl, &m); + if (r < 0) + return r; + if (!m) + goto null_message; + + if (sd_netlink_message_is_broadcast(m)) { + r = process_match(rtnl, m); + if (r != 0) + goto null_message; + } else { + r = process_reply(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_netlink_process(sd_netlink *rtnl, sd_netlink_message **ret) { + NETLINK_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_netlink *rtnl, bool need_more, uint64_t timeout_usec) { + struct pollfd p[1] = {}; + struct timespec ts; + usec_t m = USEC_INFINITY; + int r, e; + + assert(rtnl); + + e = sd_netlink_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. */ + e |= POLLIN; + else { + usec_t until; + /* Caller wants to process if there is something to + * process, but doesn't care otherwise */ + + r = sd_netlink_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_netlink_wait(sd_netlink *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_netlink_call_async(sd_netlink *nl, + sd_netlink_message *m, + sd_netlink_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_ops); + 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_netlink_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_netlink_call_async_cancel(nl, c->serial); + return r; + } + } + + if (serial) + *serial = s; + + return k; +} + +int sd_netlink_call_async_cancel(sd_netlink *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_netlink_call(sd_netlink *rtnl, + sd_netlink_message *message, + uint64_t usec, + sd_netlink_message **ret) { + usec_t timeout; + uint32_t serial; + int r; + + assert_return(rtnl, -EINVAL); + assert_return(!rtnl_pid_changed(rtnl), -ECHILD); + assert_return(message, -EINVAL); + + r = sd_netlink_send(rtnl, message, &serial); + if (r < 0) + return r; + + timeout = calc_elapse(usec); + + for (;;) { + usec_t left; + unsigned i; + + for (i = 0; i < rtnl->rqueue_size; i++) { + uint32_t received_serial; + + received_serial = rtnl_message_get_serial(rtnl->rqueue[i]); + + if (received_serial == serial) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *incoming = NULL; + uint16_t type; + + incoming = rtnl->rqueue[i]; + + /* found a match, remove from rqueue and return it */ + memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1, + sizeof(sd_netlink_message*) * (rtnl->rqueue_size - i - 1)); + rtnl->rqueue_size--; + + r = sd_netlink_message_get_errno(incoming); + if (r < 0) + return r; + + r = sd_netlink_message_get_type(incoming, &type); + if (r < 0) + return r; + + if (type == NLMSG_DONE) { + *ret = NULL; + return 0; + } + + if (ret) { + *ret = incoming; + incoming = NULL; + } + + return 1; + } + } + + r = socket_read_message(rtnl); + if (r < 0) + return r; + if (r > 0) + /* received message, so try to process straight away */ + 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(rtnl, true, left); + if (r < 0) + return r; + else if (r == 0) + return -ETIMEDOUT; + } +} + +int sd_netlink_get_events(sd_netlink *rtnl) { + assert_return(rtnl, -EINVAL); + assert_return(!rtnl_pid_changed(rtnl), -ECHILD); + + if (rtnl->rqueue_size == 0) + return POLLIN; + else + return 0; +} + +int sd_netlink_get_timeout(sd_netlink *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_netlink *rtnl = userdata; + int r; + + assert(rtnl); + + r = sd_netlink_process(rtnl, NULL); + if (r < 0) + return r; + + return 1; +} + +static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) { + sd_netlink *rtnl = userdata; + int r; + + assert(rtnl); + + r = sd_netlink_process(rtnl, NULL); + if (r < 0) + return r; + + return 1; +} + +static int prepare_callback(sd_event_source *s, void *userdata) { + sd_netlink *rtnl = userdata; + int r, e; + usec_t until; + + assert(s); + assert(rtnl); + + e = sd_netlink_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_netlink_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; +} + +int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int64_t 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->io_event_source, rtnl->fd, 0, io_callback, rtnl); + 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_description(rtnl->io_event_source, "rtnl-receive-message"); + 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_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(rtnl->time_event_source, priority); + if (r < 0) + goto fail; + + r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer"); + if (r < 0) + goto fail; + + return 0; + +fail: + sd_netlink_detach_event(rtnl); + return r; +} + +int sd_netlink_detach_event(sd_netlink *rtnl) { + assert_return(rtnl, -EINVAL); + assert_return(rtnl->event, -ENXIO); + + rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source); + + rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source); + + rtnl->event = sd_event_unref(rtnl->event); + + return 0; +} + +int sd_netlink_add_match(sd_netlink *rtnl, + uint16_t type, + sd_netlink_message_handler_t callback, + void *userdata) { + _cleanup_free_ struct match_callback *c = NULL; + int r; + + assert_return(rtnl, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!rtnl_pid_changed(rtnl), -ECHILD); + + c = new0(struct match_callback, 1); + if (!c) + return -ENOMEM; + + c->callback = callback; + c->type = type; + c->userdata = userdata; + + switch (type) { + case RTM_NEWLINK: + case RTM_DELLINK: + r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK); + if (r < 0) + return r; + + break; + case RTM_NEWADDR: + case RTM_DELADDR: + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR); + if (r < 0) + return r; + + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_IFADDR); + if (r < 0) + return r; + + break; + case RTM_NEWROUTE: + case RTM_DELROUTE: + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_ROUTE); + if (r < 0) + return r; + + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_ROUTE); + if (r < 0) + return r; + break; + default: + return -EOPNOTSUPP; + } + + LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c); + + c = NULL; + + return 0; +} + +int sd_netlink_remove_match(sd_netlink *rtnl, + uint16_t type, + sd_netlink_message_handler_t callback, + void *userdata) { + struct match_callback *c; + int r; + + 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); + + switch (type) { + case RTM_NEWLINK: + case RTM_DELLINK: + r = socket_broadcast_group_unref(rtnl, RTNLGRP_LINK); + if (r < 0) + return r; + + break; + case RTM_NEWADDR: + case RTM_DELADDR: + r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_IFADDR); + if (r < 0) + return r; + + r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_IFADDR); + if (r < 0) + return r; + + break; + case RTM_NEWROUTE: + case RTM_DELROUTE: + r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_ROUTE); + if (r < 0) + return r; + + r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_ROUTE); + if (r < 0) + return r; + break; + default: + return -EOPNOTSUPP; + } + + return 1; + } + + return 0; +} diff --git a/src/libsystemd/src/sd-netlink/test-local-addresses.c b/src/libsystemd/src/sd-netlink/test-local-addresses.c new file mode 100644 index 0000000000..e0e28cc0cc --- /dev/null +++ b/src/libsystemd/src/sd-netlink/test-local-addresses.c @@ -0,0 +1,56 @@ +/*** + This file is part of systemd. + + Copyright 2014 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 . +***/ + +#include "af-list.h" +#include "alloc-util.h" +#include "in-addr-util.h" +#include "local-addresses.h" + +static void print_local_addresses(struct local_address *a, unsigned n) { + unsigned i; + + for (i = 0; i < n; i++) { + _cleanup_free_ char *b = NULL; + + assert_se(in_addr_to_string(a[i].family, &a[i].address, &b) >= 0); + printf("%s if%i scope=%i metric=%u address=%s\n", af_to_name(a[i].family), a[i].ifindex, a[i].scope, a[i].metric, b); + } +} + +int main(int argc, char *argv[]) { + struct local_address *a; + int n; + + a = NULL; + n = local_addresses(NULL, 0, AF_UNSPEC, &a); + assert_se(n >= 0); + + printf("Local Addresses:\n"); + print_local_addresses(a, (unsigned) n); + a = mfree(a); + + n = local_gateways(NULL, 0, AF_UNSPEC, &a); + assert_se(n >= 0); + + printf("Local Gateways:\n"); + print_local_addresses(a, (unsigned) n); + free(a); + + return 0; +} diff --git a/src/libsystemd/src/sd-netlink/test-netlink.c b/src/libsystemd/src/sd-netlink/test-netlink.c new file mode 100644 index 0000000000..aadd0f06a8 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/test-netlink.c @@ -0,0 +1,440 @@ +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include +#include + +#include + +#include "ether-addr-util.h" +#include "macro.h" +#include "missing.h" +#include "netlink-util.h" +#include "socket-util.h" +#include "string-util.h" +#include "util.h" + +static void test_message_link_bridge(sd_netlink *rtnl) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; + uint32_t cost; + + assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_NEWLINK, 1) >= 0); + assert_se(sd_rtnl_message_link_set_family(message, PF_BRIDGE) >= 0); + assert_se(sd_netlink_message_open_container(message, IFLA_PROTINFO) >= 0); + assert_se(sd_netlink_message_append_u32(message, IFLA_BRPORT_COST, 10) >= 0); + assert_se(sd_netlink_message_close_container(message) >= 0); + + assert_se(sd_netlink_message_rewind(message) >= 0); + + assert_se(sd_netlink_message_enter_container(message, IFLA_PROTINFO) >= 0); + assert_se(sd_netlink_message_read_u32(message, IFLA_BRPORT_COST, &cost) >= 0); + assert_se(cost == 10); + assert_se(sd_netlink_message_exit_container(message) >= 0); +} + +static void test_link_configure(sd_netlink *rtnl, int ifindex) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; + const char *mac = "98:fe:94:3f:c6:18", *name = "test"; + char buffer[ETHER_ADDR_TO_STRING_MAX]; + unsigned int mtu = 1450, mtu_out; + const char *name_out; + struct ether_addr mac_out; + + /* we'd really like to test NEWLINK, but let's not mess with the running kernel */ + assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_GETLINK, ifindex) >= 0); + assert_se(sd_netlink_message_append_string(message, IFLA_IFNAME, name) >= 0); + assert_se(sd_netlink_message_append_ether_addr(message, IFLA_ADDRESS, ether_aton(mac)) >= 0); + assert_se(sd_netlink_message_append_u32(message, IFLA_MTU, mtu) >= 0); + + assert_se(sd_netlink_call(rtnl, message, 0, NULL) == 1); + assert_se(sd_netlink_message_rewind(message) >= 0); + + assert_se(sd_netlink_message_read_string(message, IFLA_IFNAME, &name_out) >= 0); + assert_se(streq(name, name_out)); + + assert_se(sd_netlink_message_read_ether_addr(message, IFLA_ADDRESS, &mac_out) >= 0); + assert_se(streq(mac, ether_addr_to_string(&mac_out, buffer))); + + assert_se(sd_netlink_message_read_u32(message, IFLA_MTU, &mtu_out) >= 0); + assert_se(mtu == mtu_out); +} + +static void test_link_get(sd_netlink *rtnl, int ifindex) { + sd_netlink_message *m; + sd_netlink_message *r; + unsigned int mtu = 1500; + const char *str_data; + uint8_t u8_data; + uint32_t u32_data; + struct ether_addr eth_data; + + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); + assert_se(m); + + /* u8 test cases */ + assert_se(sd_netlink_message_append_u8(m, IFLA_CARRIER, 0) >= 0); + assert_se(sd_netlink_message_append_u8(m, IFLA_OPERSTATE, 0) >= 0); + assert_se(sd_netlink_message_append_u8(m, IFLA_LINKMODE, 0) >= 0); + + /* u32 test cases */ + assert_se(sd_netlink_message_append_u32(m, IFLA_MTU, mtu) >= 0); + assert_se(sd_netlink_message_append_u32(m, IFLA_GROUP, 0) >= 0); + assert_se(sd_netlink_message_append_u32(m, IFLA_TXQLEN, 0) >= 0); + assert_se(sd_netlink_message_append_u32(m, IFLA_NUM_TX_QUEUES, 0) >= 0); + assert_se(sd_netlink_message_append_u32(m, IFLA_NUM_RX_QUEUES, 0) >= 0); + + assert_se(sd_netlink_call(rtnl, m, -1, &r) == 1); + + assert_se(sd_netlink_message_read_string(r, IFLA_IFNAME, &str_data) == 0); + + assert_se(sd_netlink_message_read_u8(r, IFLA_CARRIER, &u8_data) == 0); + assert_se(sd_netlink_message_read_u8(r, IFLA_OPERSTATE, &u8_data) == 0); + assert_se(sd_netlink_message_read_u8(r, IFLA_LINKMODE, &u8_data) == 0); + + assert_se(sd_netlink_message_read_u32(r, IFLA_MTU, &u32_data) == 0); + assert_se(sd_netlink_message_read_u32(r, IFLA_GROUP, &u32_data) == 0); + assert_se(sd_netlink_message_read_u32(r, IFLA_TXQLEN, &u32_data) == 0); + assert_se(sd_netlink_message_read_u32(r, IFLA_NUM_TX_QUEUES, &u32_data) == 0); + assert_se(sd_netlink_message_read_u32(r, IFLA_NUM_RX_QUEUES, &u32_data) == 0); + + assert_se(sd_netlink_message_read_ether_addr(r, IFLA_ADDRESS, ð_data) == 0); + + assert_se((m = sd_netlink_message_unref(m)) == NULL); + assert_se((r = sd_netlink_message_unref(r)) == NULL); +} + + +static void test_address_get(sd_netlink *rtnl, int ifindex) { + sd_netlink_message *m; + sd_netlink_message *r; + struct in_addr in_data; + struct ifa_cacheinfo cache; + const char *label; + + assert_se(sd_rtnl_message_new_addr(rtnl, &m, RTM_GETADDR, ifindex, AF_INET) >= 0); + assert_se(m); + + assert_se(sd_netlink_call(rtnl, m, -1, &r) == 1); + + assert_se(sd_netlink_message_read_in_addr(r, IFA_LOCAL, &in_data) == 0); + assert_se(sd_netlink_message_read_in_addr(r, IFA_ADDRESS, &in_data) == 0); + assert_se(sd_netlink_message_read_string(r, IFA_LABEL, &label) == 0); + assert_se(sd_netlink_message_read_cache_info(r, IFA_CACHEINFO, &cache) == 0); + + assert_se((m = sd_netlink_message_unref(m)) == NULL); + assert_se((r = sd_netlink_message_unref(r)) == NULL); + +} + +static void test_route(void) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req; + struct in_addr addr, addr_data; + uint32_t index = 2, u32_data; + int r; + + r = sd_rtnl_message_new_route(NULL, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC); + if (r < 0) { + log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); + return; + } + + addr.s_addr = htonl(INADDR_LOOPBACK); + + r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &addr); + if (r < 0) { + log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); + return; + } + + r = sd_netlink_message_append_u32(req, RTA_OIF, index); + if (r < 0) { + log_error_errno(r, "Could not append RTA_OIF attribute: %m"); + return; + } + + assert_se(sd_netlink_message_rewind(req) >= 0); + + assert_se(sd_netlink_message_read_in_addr(req, RTA_GATEWAY, &addr_data) >= 0); + assert_se(addr_data.s_addr == addr.s_addr); + + assert_se(sd_netlink_message_read_u32(req, RTA_OIF, &u32_data) >= 0); + assert_se(u32_data == index); + + assert_se((req = sd_netlink_message_unref(req)) == NULL); +} + +static void test_multiple(void) { + sd_netlink *rtnl1, *rtnl2; + + assert_se(sd_netlink_open(&rtnl1) >= 0); + assert_se(sd_netlink_open(&rtnl2) >= 0); + + rtnl1 = sd_netlink_unref(rtnl1); + rtnl2 = sd_netlink_unref(rtnl2); +} + +static int link_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + char *ifname = userdata; + const char *data; + + assert_se(rtnl); + assert_se(m); + + log_info("got link info about %s", ifname); + free(ifname); + + assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &data) >= 0); + assert_se(streq(data, "lo")); + + return 1; +} + +static void test_event_loop(int ifindex) { + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + char *ifname; + + ifname = strdup("lo2"); + assert_se(ifname); + + assert_se(sd_netlink_open(&rtnl) >= 0); + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); + + assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, NULL) >= 0); + + assert_se(sd_event_default(&event) >= 0); + + assert_se(sd_netlink_attach_event(rtnl, event, 0) >= 0); + + assert_se(sd_event_run(event, 0) >= 0); + + assert_se(sd_netlink_detach_event(rtnl) >= 0); + + assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); +} + +static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + int *counter = userdata; + int r; + + (*counter)--; + + r = sd_netlink_message_get_errno(m); + + log_info_errno(r, "%d left in pipe. got reply: %m", *counter); + + assert_se(r >= 0); + + return 1; +} + +static void test_async(int ifindex) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *r = NULL; + uint32_t serial; + char *ifname; + + ifname = strdup("lo"); + assert_se(ifname); + + assert_se(sd_netlink_open(&rtnl) >= 0); + + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); + + assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, &serial) >= 0); + + assert_se(sd_netlink_wait(rtnl, 0) >= 0); + assert_se(sd_netlink_process(rtnl, &r) >= 0); + + assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); +} + +static void test_pipe(int ifindex) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m1 = NULL, *m2 = NULL; + int counter = 0; + + assert_se(sd_netlink_open(&rtnl) >= 0); + + assert_se(sd_rtnl_message_new_link(rtnl, &m1, RTM_GETLINK, ifindex) >= 0); + assert_se(sd_rtnl_message_new_link(rtnl, &m2, RTM_GETLINK, ifindex) >= 0); + + counter++; + assert_se(sd_netlink_call_async(rtnl, m1, pipe_handler, &counter, 0, NULL) >= 0); + + counter++; + assert_se(sd_netlink_call_async(rtnl, m2, pipe_handler, &counter, 0, NULL) >= 0); + + while (counter > 0) { + assert_se(sd_netlink_wait(rtnl, 0) >= 0); + assert_se(sd_netlink_process(rtnl, NULL) >= 0); + } + + assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); +} + +static void test_container(void) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + uint16_t u16_data; + uint32_t u32_data; + const char *string_data; + + assert_se(sd_rtnl_message_new_link(NULL, &m, RTM_NEWLINK, 0) >= 0); + + assert_se(sd_netlink_message_open_container(m, IFLA_LINKINFO) >= 0); + assert_se(sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "vlan") >= 0); + assert_se(sd_netlink_message_append_u16(m, IFLA_VLAN_ID, 100) >= 0); + assert_se(sd_netlink_message_close_container(m) >= 0); + assert_se(sd_netlink_message_append_string(m, IFLA_INFO_KIND, "vlan") >= 0); + assert_se(sd_netlink_message_close_container(m) >= 0); + assert_se(sd_netlink_message_close_container(m) == -EINVAL); + + assert_se(sd_netlink_message_rewind(m) >= 0); + + assert_se(sd_netlink_message_enter_container(m, IFLA_LINKINFO) >= 0); + assert_se(sd_netlink_message_read_string(m, IFLA_INFO_KIND, &string_data) >= 0); + assert_se(streq("vlan", string_data)); + + assert_se(sd_netlink_message_enter_container(m, IFLA_INFO_DATA) >= 0); + assert_se(sd_netlink_message_read_u16(m, IFLA_VLAN_ID, &u16_data) >= 0); + assert_se(sd_netlink_message_exit_container(m) >= 0); + + assert_se(sd_netlink_message_read_string(m, IFLA_INFO_KIND, &string_data) >= 0); + assert_se(streq("vlan", string_data)); + assert_se(sd_netlink_message_exit_container(m) >= 0); + + assert_se(sd_netlink_message_read_u32(m, IFLA_LINKINFO, &u32_data) < 0); + + assert_se(sd_netlink_message_exit_container(m) == -EINVAL); +} + +static void test_match(void) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + + assert_se(sd_netlink_open(&rtnl) >= 0); + + assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, link_handler, NULL) >= 0); + assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, link_handler, NULL) >= 0); + + assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 1); + assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 1); + assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 0); + + assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); +} + +static void test_get_addresses(sd_netlink *rtnl) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + sd_netlink_message *m; + + assert_se(sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC) >= 0); + + assert_se(sd_netlink_call(rtnl, req, 0, &reply) >= 0); + + for (m = reply; m; m = sd_netlink_message_next(m)) { + uint16_t type; + unsigned char scope, flags; + int family, ifindex; + + assert_se(sd_netlink_message_get_type(m, &type) >= 0); + assert_se(type == RTM_NEWADDR); + + assert_se(sd_rtnl_message_addr_get_ifindex(m, &ifindex) >= 0); + assert_se(sd_rtnl_message_addr_get_family(m, &family) >= 0); + assert_se(sd_rtnl_message_addr_get_scope(m, &scope) >= 0); + assert_se(sd_rtnl_message_addr_get_flags(m, &flags) >= 0); + + assert_se(ifindex > 0); + assert_se(family == AF_INET || family == AF_INET6); + + log_info("got IPv%u address on ifindex %i", family == AF_INET ? 4: 6, ifindex); + } +} + +static void test_message(void) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + + assert_se(rtnl_message_new_synthetic_error(-ETIMEDOUT, 1, &m) >= 0); + assert_se(sd_netlink_message_get_errno(m) == -ETIMEDOUT); +} + +int main(void) { + sd_netlink *rtnl; + sd_netlink_message *m; + sd_netlink_message *r; + const char *string_data; + int if_loopback; + uint16_t type; + + test_message(); + + test_match(); + + test_multiple(); + + test_route(); + + test_container(); + + assert_se(sd_netlink_open(&rtnl) >= 0); + assert_se(rtnl); + + if_loopback = (int) if_nametoindex("lo"); + assert_se(if_loopback > 0); + + test_async(if_loopback); + + test_pipe(if_loopback); + + test_event_loop(if_loopback); + + test_link_configure(rtnl, if_loopback); + + test_get_addresses(rtnl); + + test_message_link_bridge(rtnl); + + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, if_loopback) >= 0); + assert_se(m); + + assert_se(sd_netlink_message_get_type(m, &type) >= 0); + assert_se(type == RTM_GETLINK); + + assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &string_data) == -EPERM); + + assert_se(sd_netlink_call(rtnl, m, 0, &r) == 1); + assert_se(sd_netlink_message_get_type(r, &type) >= 0); + assert_se(type == RTM_NEWLINK); + + assert_se((r = sd_netlink_message_unref(r)) == NULL); + + assert_se(sd_netlink_call(rtnl, m, -1, &r) == -EPERM); + assert_se((m = sd_netlink_message_unref(m)) == NULL); + assert_se((r = sd_netlink_message_unref(r)) == NULL); + + test_link_get(rtnl, if_loopback); + test_address_get(rtnl, if_loopback); + + assert_se((m = sd_netlink_message_unref(m)) == NULL); + assert_se((r = sd_netlink_message_unref(r)) == NULL); + assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); + + return EXIT_SUCCESS; +} diff --git a/src/libsystemd/src/sd-network/network-util.c b/src/libsystemd/src/sd-network/network-util.c new file mode 100644 index 0000000000..a0d9b5f1a4 --- /dev/null +++ b/src/libsystemd/src/sd-network/network-util.c @@ -0,0 +1,37 @@ +/*** + This file is part of systemd. + + Copyright 2014 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 . +***/ + +#include "alloc-util.h" +#include "fd-util.h" +#include "network-util.h" +#include "strv.h" + +bool network_is_online(void) { + _cleanup_free_ char *state = NULL; + int r; + + r = sd_network_get_operational_state(&state); + if (r < 0) /* if we don't know anything, we consider the system online */ + return true; + + if (STR_IN_SET(state, "routable", "degraded")) + return true; + + return false; +} diff --git a/src/libsystemd/src/sd-network/network-util.h b/src/libsystemd/src/sd-network/network-util.h new file mode 100644 index 0000000000..8c4dbc68b1 --- /dev/null +++ b/src/libsystemd/src/sd-network/network-util.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Thomas Hindø Paabøl Andersen + + 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 . +***/ + +#include + +bool network_is_online(void); diff --git a/src/libsystemd/src/sd-network/sd-network.c b/src/libsystemd/src/sd-network/sd-network.c new file mode 100644 index 0000000000..70083213f9 --- /dev/null +++ b/src/libsystemd/src/sd-network/sd-network.c @@ -0,0 +1,400 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + Copyright 2014 Tom Gundersen + + 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 . +***/ + +#include +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "macro.h" +#include "parse-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "strv.h" +#include "util.h" + +_public_ int sd_network_get_operational_state(char **state) { + _cleanup_free_ char *s = NULL; + int r; + + assert_return(state, -EINVAL); + + r = parse_env_file("/run/systemd/netif/state", NEWLINE, "OPER_STATE", &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + *state = s; + s = NULL; + + return 0; +} + +static int network_get_strv(const char *key, char ***ret) { + _cleanup_strv_free_ char **a = NULL; + _cleanup_free_ char *s = NULL; + int r; + + assert_return(ret, -EINVAL); + + r = parse_env_file("/run/systemd/netif/state", NEWLINE, key, &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) { + *ret = NULL; + return 0; + } + + a = strv_split(s, " "); + if (!a) + return -ENOMEM; + + strv_uniq(a); + r = strv_length(a); + + *ret = a; + a = NULL; + + return r; +} + +_public_ int sd_network_get_dns(char ***ret) { + return network_get_strv("DNS", ret); +} + +_public_ int sd_network_get_ntp(char ***ret) { + return network_get_strv("NTP", ret); +} + +_public_ int sd_network_get_search_domains(char ***ret) { + return network_get_strv("DOMAINS", ret); +} + +_public_ int sd_network_get_route_domains(char ***ret) { + return network_get_strv("ROUTE_DOMAINS", ret); +} + +static int network_link_get_string(int ifindex, const char *field, char **ret) { + char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; + _cleanup_free_ char *s = NULL; + int r; + + assert_return(ifindex > 0, -EINVAL); + assert_return(ret, -EINVAL); + + xsprintf(path, "/run/systemd/netif/links/%i", ifindex); + + r = parse_env_file(path, NEWLINE, field, &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + *ret = s; + s = NULL; + + return 0; +} + +static int network_link_get_strv(int ifindex, const char *key, char ***ret) { + char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; + _cleanup_strv_free_ char **a = NULL; + _cleanup_free_ char *s = NULL; + int r; + + assert_return(ifindex > 0, -EINVAL); + assert_return(ret, -EINVAL); + + xsprintf(path, "/run/systemd/netif/links/%i", ifindex); + r = parse_env_file(path, NEWLINE, key, &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) { + *ret = NULL; + return 0; + } + + a = strv_split(s, " "); + if (!a) + return -ENOMEM; + + strv_uniq(a); + r = strv_length(a); + + *ret = a; + a = NULL; + + return r; +} + +_public_ int sd_network_link_get_setup_state(int ifindex, char **state) { + return network_link_get_string(ifindex, "ADMIN_STATE", state); +} + +_public_ int sd_network_link_get_network_file(int ifindex, char **filename) { + return network_link_get_string(ifindex, "NETWORK_FILE", filename); +} + +_public_ int sd_network_link_get_operational_state(int ifindex, char **state) { + return network_link_get_string(ifindex, "OPER_STATE", state); +} + +_public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) { + return network_link_get_string(ifindex, "LLMNR", llmnr); +} + +_public_ int sd_network_link_get_mdns(int ifindex, char **mdns) { + return network_link_get_string(ifindex, "MDNS", mdns); +} + +_public_ int sd_network_link_get_dnssec(int ifindex, char **dnssec) { + return network_link_get_string(ifindex, "DNSSEC", dnssec); +} + +_public_ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta) { + return network_link_get_strv(ifindex, "DNSSEC_NTA", nta); +} + +_public_ int sd_network_link_get_timezone(int ifindex, char **ret) { + return network_link_get_string(ifindex, "TIMEZONE", ret); +} + +_public_ int sd_network_link_get_dns(int ifindex, char ***ret) { + return network_link_get_strv(ifindex, "DNS", ret); +} + +_public_ int sd_network_link_get_ntp(int ifindex, char ***ret) { + return network_link_get_strv(ifindex, "NTP", ret); +} + +_public_ int sd_network_link_get_search_domains(int ifindex, char ***ret) { + return network_link_get_strv(ifindex, "DOMAINS", ret); +} + +_public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) { + return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret); +} + +static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) { + char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; + _cleanup_free_ int *ifis = NULL; + _cleanup_free_ char *s = NULL; + size_t allocated = 0, c = 0; + const char *x; + int r; + + assert_return(ifindex > 0, -EINVAL); + assert_return(ret, -EINVAL); + + xsprintf(path, "/run/systemd/netif/links/%i", ifindex); + r = parse_env_file(path, NEWLINE, key, &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) { + *ret = NULL; + return 0; + } + + x = s; + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&x, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + r = parse_ifindex(word, &ifindex); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(ifis, allocated, c + 1)) + return -ENOMEM; + + ifis[c++] = ifindex; + } + + if (!GREEDY_REALLOC(ifis, allocated, c + 1)) + return -ENOMEM; + ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/ + + *ret = ifis; + ifis = NULL; + + return c; +} + +_public_ int sd_network_link_get_carrier_bound_to(int ifindex, int **ret) { + return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_TO", ret); +} + +_public_ int sd_network_link_get_carrier_bound_by(int ifindex, int **ret) { + return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_BY", ret); +} + +static inline int MONITOR_TO_FD(sd_network_monitor *m) { + return (int) (unsigned long) m - 1; +} + +static inline sd_network_monitor* FD_TO_MONITOR(int fd) { + return (sd_network_monitor*) (unsigned long) (fd + 1); +} + +static int monitor_add_inotify_watch(int fd) { + int k; + + k = inotify_add_watch(fd, "/run/systemd/netif/links/", IN_MOVED_TO|IN_DELETE); + if (k >= 0) + return 0; + else if (errno != ENOENT) + return -errno; + + k = inotify_add_watch(fd, "/run/systemd/netif/", IN_CREATE|IN_ISDIR); + if (k >= 0) + return 0; + else if (errno != ENOENT) + return -errno; + + k = inotify_add_watch(fd, "/run/systemd/", IN_CREATE|IN_ISDIR); + if (k < 0) + return -errno; + + return 0; +} + +_public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category) { + _cleanup_close_ int fd = -1; + int k; + bool good = false; + + assert_return(m, -EINVAL); + + fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (fd < 0) + return -errno; + + if (!category || streq(category, "links")) { + k = monitor_add_inotify_watch(fd); + if (k < 0) + return k; + + good = true; + } + + if (!good) + return -EINVAL; + + *m = FD_TO_MONITOR(fd); + fd = -1; + + return 0; +} + +_public_ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) { + int fd; + + if (m) { + fd = MONITOR_TO_FD(m); + close_nointr(fd); + } + + return NULL; +} + +_public_ int sd_network_monitor_flush(sd_network_monitor *m) { + union inotify_event_buffer buffer; + struct inotify_event *e; + ssize_t l; + int fd, k; + + assert_return(m, -EINVAL); + + fd = MONITOR_TO_FD(m); + + l = read(fd, &buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return -errno; + } + + FOREACH_INOTIFY_EVENT(e, buffer, l) { + if (e->mask & IN_ISDIR) { + k = monitor_add_inotify_watch(fd); + if (k < 0) + return k; + + k = inotify_rm_watch(fd, e->wd); + if (k < 0) + return -errno; + } + } + + return 0; +} + +_public_ int sd_network_monitor_get_fd(sd_network_monitor *m) { + + assert_return(m, -EINVAL); + + return MONITOR_TO_FD(m); +} + +_public_ int sd_network_monitor_get_events(sd_network_monitor *m) { + + assert_return(m, -EINVAL); + + /* For now we will only return POLLIN here, since we don't + * need anything else ever for inotify. However, let's have + * this API to keep our options open should we later on need + * it. */ + return POLLIN; +} + +_public_ int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec) { + + assert_return(m, -EINVAL); + assert_return(timeout_usec, -EINVAL); + + /* For now we will only return (uint64_t) -1, since we don't + * need any timeout. However, let's have this API to keep our + * options open should we later on need it. */ + *timeout_usec = (uint64_t) -1; + return 0; +} diff --git a/src/libsystemd/src/sd-path/sd-path.c b/src/libsystemd/src/sd-path/sd-path.c new file mode 100644 index 0000000000..6d9f3e2a61 --- /dev/null +++ b/src/libsystemd/src/sd-path/sd-path.c @@ -0,0 +1,638 @@ +/*** + This file is part of systemd. + + Copyright 2014 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 . +***/ + +#include + +#include "alloc-util.h" +#include "architecture.h" +#include "fd-util.h" +#include "fileio.h" +#include "missing.h" +#include "path-util.h" +#include "string-util.h" +#include "strv.h" +#include "user-util.h" +#include "util.h" + +static int from_environment(const char *envname, const char *fallback, const char **ret) { + assert(ret); + + if (envname) { + const char *e; + + e = secure_getenv(envname); + if (e && path_is_absolute(e)) { + *ret = e; + return 0; + } + } + + if (fallback) { + *ret = fallback; + return 0; + } + + return -ENXIO; +} + +static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) { + _cleanup_free_ char *h = NULL; + char *cc = NULL; + int r; + + assert(suffix); + assert(buffer); + assert(ret); + + if (envname) { + const char *e = NULL; + + e = secure_getenv(envname); + if (e && path_is_absolute(e)) { + *ret = e; + return 0; + } + } + + r = get_home_dir(&h); + if (r < 0) + return r; + + if (endswith(h, "/")) + cc = strappend(h, suffix); + else + cc = strjoin(h, "/", suffix, NULL); + if (!cc) + return -ENOMEM; + + *buffer = cc; + *ret = cc; + return 0; +} + +static int from_user_dir(const char *field, char **buffer, const char **ret) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *b = NULL; + _cleanup_free_ const char *fn = NULL; + const char *c = NULL; + char line[LINE_MAX]; + size_t n; + int r; + + assert(field); + assert(buffer); + assert(ret); + + r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c); + if (r < 0) + return r; + + fn = strappend(c, "/user-dirs.dirs"); + if (!fn) + return -ENOMEM; + + f = fopen(fn, "re"); + if (!f) { + if (errno == ENOENT) + goto fallback; + + return -errno; + } + + /* This is an awful parse, but it follows closely what + * xdg-user-dirs does upstream */ + + n = strlen(field); + FOREACH_LINE(line, f, return -errno) { + char *l, *p, *e; + + l = strstrip(line); + + if (!strneq(l, field, n)) + continue; + + p = l + n; + p += strspn(p, WHITESPACE); + + if (*p != '=') + continue; + p++; + + p += strspn(p, WHITESPACE); + + if (*p != '"') + continue; + p++; + + e = strrchr(p, '"'); + if (!e) + continue; + *e = 0; + + /* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */ + if (startswith(p, "$HOME/")) { + _cleanup_free_ char *h = NULL; + char *cc; + + r = get_home_dir(&h); + if (r < 0) + return r; + + cc = strappend(h, p+5); + if (!cc) + return -ENOMEM; + + *buffer = cc; + *ret = cc; + return 0; + } else if (streq(p, "$HOME")) { + + r = get_home_dir(buffer); + if (r < 0) + return r; + + *ret = *buffer; + return 0; + } else if (path_is_absolute(p)) { + char *copy; + + copy = strdup(p); + if (!copy) + return -ENOMEM; + + *buffer = copy; + *ret = copy; + return 0; + } + } + +fallback: + /* The desktop directory defaults to $HOME/Desktop, the others to $HOME */ + if (streq(field, "XDG_DESKTOP_DIR")) { + _cleanup_free_ char *h = NULL; + char *cc; + + r = get_home_dir(&h); + if (r < 0) + return r; + + cc = strappend(h, "/Desktop"); + if (!cc) + return -ENOMEM; + + *buffer = cc; + *ret = cc; + } else { + + r = get_home_dir(buffer); + if (r < 0) + return r; + + *ret = *buffer; + } + + return 0; +} + +static int get_path(uint64_t type, char **buffer, const char **ret) { + int r; + + assert(buffer); + assert(ret); + + switch (type) { + + case SD_PATH_TEMPORARY: + return from_environment("TMPDIR", "/tmp", ret); + + case SD_PATH_TEMPORARY_LARGE: + return from_environment("TMPDIR", "/var/tmp", ret); + + case SD_PATH_SYSTEM_BINARIES: + *ret = "/usr/bin"; + return 0; + + case SD_PATH_SYSTEM_INCLUDE: + *ret = "/usr/include"; + return 0; + + case SD_PATH_SYSTEM_LIBRARY_PRIVATE: + *ret = "/usr/lib"; + return 0; + + case SD_PATH_SYSTEM_LIBRARY_ARCH: + *ret = LIBDIR; + return 0; + + case SD_PATH_SYSTEM_SHARED: + *ret = "/usr/share"; + return 0; + + case SD_PATH_SYSTEM_CONFIGURATION_FACTORY: + *ret = "/usr/share/factory/etc"; + return 0; + + case SD_PATH_SYSTEM_STATE_FACTORY: + *ret = "/usr/share/factory/var"; + return 0; + + case SD_PATH_SYSTEM_CONFIGURATION: + *ret = "/etc"; + return 0; + + case SD_PATH_SYSTEM_RUNTIME: + *ret = "/run"; + return 0; + + case SD_PATH_SYSTEM_RUNTIME_LOGS: + *ret = "/run/log"; + return 0; + + case SD_PATH_SYSTEM_STATE_PRIVATE: + *ret = "/var/lib"; + return 0; + + case SD_PATH_SYSTEM_STATE_LOGS: + *ret = "/var/log"; + return 0; + + case SD_PATH_SYSTEM_STATE_CACHE: + *ret = "/var/cache"; + return 0; + + case SD_PATH_SYSTEM_STATE_SPOOL: + *ret = "/var/spool"; + return 0; + + case SD_PATH_USER_BINARIES: + return from_home_dir(NULL, ".local/bin", buffer, ret); + + case SD_PATH_USER_LIBRARY_PRIVATE: + return from_home_dir(NULL, ".local/lib", buffer, ret); + + case SD_PATH_USER_LIBRARY_ARCH: + return from_home_dir(NULL, ".local/lib/" LIB_ARCH_TUPLE, buffer, ret); + + case SD_PATH_USER_SHARED: + return from_home_dir("XDG_DATA_HOME", ".local/share", buffer, ret); + + case SD_PATH_USER_CONFIGURATION: + return from_home_dir("XDG_CONFIG_HOME", ".config", buffer, ret); + + case SD_PATH_USER_RUNTIME: + return from_environment("XDG_RUNTIME_DIR", NULL, ret); + + case SD_PATH_USER_STATE_CACHE: + return from_home_dir("XDG_CACHE_HOME", ".cache", buffer, ret); + + case SD_PATH_USER: + r = get_home_dir(buffer); + if (r < 0) + return r; + + *ret = *buffer; + return 0; + + case SD_PATH_USER_DOCUMENTS: + return from_user_dir("XDG_DOCUMENTS_DIR", buffer, ret); + + case SD_PATH_USER_MUSIC: + return from_user_dir("XDG_MUSIC_DIR", buffer, ret); + + case SD_PATH_USER_PICTURES: + return from_user_dir("XDG_PICTURES_DIR", buffer, ret); + + case SD_PATH_USER_VIDEOS: + return from_user_dir("XDG_VIDEOS_DIR", buffer, ret); + + case SD_PATH_USER_DOWNLOAD: + return from_user_dir("XDG_DOWNLOAD_DIR", buffer, ret); + + case SD_PATH_USER_PUBLIC: + return from_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret); + + case SD_PATH_USER_TEMPLATES: + return from_user_dir("XDG_TEMPLATES_DIR", buffer, ret); + + case SD_PATH_USER_DESKTOP: + return from_user_dir("XDG_DESKTOP_DIR", buffer, ret); + } + + return -EOPNOTSUPP; +} + +_public_ int sd_path_home(uint64_t type, const char *suffix, char **path) { + char *buffer = NULL, *cc; + const char *ret; + int r; + + assert_return(path, -EINVAL); + + if (IN_SET(type, + SD_PATH_SEARCH_BINARIES, + SD_PATH_SEARCH_LIBRARY_PRIVATE, + SD_PATH_SEARCH_LIBRARY_ARCH, + SD_PATH_SEARCH_SHARED, + SD_PATH_SEARCH_CONFIGURATION_FACTORY, + SD_PATH_SEARCH_STATE_FACTORY, + SD_PATH_SEARCH_CONFIGURATION)) { + + _cleanup_strv_free_ char **l = NULL; + + r = sd_path_search(type, suffix, &l); + if (r < 0) + return r; + + buffer = strv_join(l, ":"); + if (!buffer) + return -ENOMEM; + + *path = buffer; + return 0; + } + + r = get_path(type, &buffer, &ret); + if (r < 0) + return r; + + if (!suffix) { + if (!buffer) { + buffer = strdup(ret); + if (!buffer) + return -ENOMEM; + } + + *path = buffer; + return 0; + } + + suffix += strspn(suffix, "/"); + + if (endswith(ret, "/")) + cc = strappend(ret, suffix); + else + cc = strjoin(ret, "/", suffix, NULL); + + free(buffer); + + if (!cc) + return -ENOMEM; + + *path = cc; + return 0; +} + +static int search_from_environment( + char ***list, + const char *env_home, + const char *home_suffix, + const char *env_search, + bool env_search_sufficient, + const char *first, ...) { + + const char *e; + char *h = NULL; + char **l = NULL; + int r; + + assert(list); + + if (env_search) { + e = secure_getenv(env_search); + if (e) { + l = strv_split(e, ":"); + if (!l) + return -ENOMEM; + + if (env_search_sufficient) { + *list = l; + return 0; + } + } + } + + if (!l && first) { + va_list ap; + + va_start(ap, first); + l = strv_new_ap(first, ap); + va_end(ap); + + if (!l) + return -ENOMEM; + } + + if (env_home) { + e = secure_getenv(env_home); + if (e && path_is_absolute(e)) { + h = strdup(e); + if (!h) { + strv_free(l); + return -ENOMEM; + } + } + } + + if (!h && home_suffix) { + e = secure_getenv("HOME"); + if (e && path_is_absolute(e)) { + if (endswith(e, "/")) + h = strappend(e, home_suffix); + else + h = strjoin(e, "/", home_suffix, NULL); + + if (!h) { + strv_free(l); + return -ENOMEM; + } + } + } + + if (h) { + r = strv_consume_prepend(&l, h); + if (r < 0) { + strv_free(l); + return -ENOMEM; + } + } + + *list = l; + return 0; +} + +static int get_search(uint64_t type, char ***list) { + + assert(list); + + switch(type) { + + case SD_PATH_SEARCH_BINARIES: + return search_from_environment(list, + NULL, + ".local/bin", + "PATH", + true, + "/usr/local/sbin", + "/usr/local/bin", + "/usr/sbin", + "/usr/bin", +#ifdef HAVE_SPLIT_USR + "/sbin", + "/bin", +#endif + NULL); + + case SD_PATH_SEARCH_LIBRARY_PRIVATE: + return search_from_environment(list, + NULL, + ".local/lib", + NULL, + false, + "/usr/local/lib", + "/usr/lib", +#ifdef HAVE_SPLIT_USR + "/lib", +#endif + NULL); + + case SD_PATH_SEARCH_LIBRARY_ARCH: + return search_from_environment(list, + NULL, + ".local/lib/" LIB_ARCH_TUPLE, + "LD_LIBRARY_PATH", + true, + LIBDIR, +#ifdef HAVE_SPLIT_USR + ROOTLIBDIR, +#endif + NULL); + + case SD_PATH_SEARCH_SHARED: + return search_from_environment(list, + "XDG_DATA_HOME", + ".local/share", + "XDG_DATA_DIRS", + false, + "/usr/local/share", + "/usr/share", + NULL); + + case SD_PATH_SEARCH_CONFIGURATION_FACTORY: + return search_from_environment(list, + NULL, + NULL, + NULL, + false, + "/usr/local/share/factory/etc", + "/usr/share/factory/etc", + NULL); + + case SD_PATH_SEARCH_STATE_FACTORY: + return search_from_environment(list, + NULL, + NULL, + NULL, + false, + "/usr/local/share/factory/var", + "/usr/share/factory/var", + NULL); + + case SD_PATH_SEARCH_CONFIGURATION: + return search_from_environment(list, + "XDG_CONFIG_HOME", + ".config", + "XDG_CONFIG_DIRS", + false, + "/etc", + NULL); + } + + return -EOPNOTSUPP; +} + +_public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) { + char **l, **i, **j, **n; + int r; + + assert_return(paths, -EINVAL); + + if (!IN_SET(type, + SD_PATH_SEARCH_BINARIES, + SD_PATH_SEARCH_LIBRARY_PRIVATE, + SD_PATH_SEARCH_LIBRARY_ARCH, + SD_PATH_SEARCH_SHARED, + SD_PATH_SEARCH_CONFIGURATION_FACTORY, + SD_PATH_SEARCH_STATE_FACTORY, + SD_PATH_SEARCH_CONFIGURATION)) { + + char *p; + + r = sd_path_home(type, suffix, &p); + if (r < 0) + return r; + + l = new(char*, 2); + if (!l) { + free(p); + return -ENOMEM; + } + + l[0] = p; + l[1] = NULL; + + *paths = l; + return 0; + } + + r = get_search(type, &l); + if (r < 0) + return r; + + if (!suffix) { + *paths = l; + return 0; + } + + n = new(char*, strv_length(l)+1); + if (!n) { + strv_free(l); + return -ENOMEM; + } + + j = n; + STRV_FOREACH(i, l) { + + if (endswith(*i, "/")) + *j = strappend(*i, suffix); + else + *j = strjoin(*i, "/", suffix, NULL); + + if (!*j) { + strv_free(l); + strv_free(n); + return -ENOMEM; + } + + j++; + } + + *j = NULL; + *paths = n; + return 0; +} diff --git a/src/libsystemd/src/sd-resolve/sd-resolve.c b/src/libsystemd/src/sd-resolve/sd-resolve.c new file mode 100644 index 0000000000..6eacf2b69a --- /dev/null +++ b/src/libsystemd/src/sd-resolve/sd-resolve.c @@ -0,0 +1,1245 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "fd-util.h" +#include "io-util.h" +#include "list.h" +#include "missing.h" +#include "socket-util.h" +#include "util.h" + +#define WORKERS_MIN 1U +#define WORKERS_MAX 16U +#define QUERIES_MAX 256U +#define BUFSIZE 10240U + +typedef enum { + REQUEST_ADDRINFO, + RESPONSE_ADDRINFO, + REQUEST_NAMEINFO, + RESPONSE_NAMEINFO, + REQUEST_TERMINATE, + RESPONSE_DIED +} QueryType; + +enum { + REQUEST_RECV_FD, + REQUEST_SEND_FD, + RESPONSE_RECV_FD, + RESPONSE_SEND_FD, + _FD_MAX +}; + +struct sd_resolve { + unsigned n_ref; + + bool dead:1; + pid_t original_pid; + + int fds[_FD_MAX]; + + pthread_t workers[WORKERS_MAX]; + unsigned n_valid_workers; + + unsigned current_id; + sd_resolve_query* query_array[QUERIES_MAX]; + unsigned n_queries, n_done, n_outstanding; + + sd_event_source *event_source; + sd_event *event; + + sd_resolve_query *current; + + sd_resolve **default_resolve_ptr; + pid_t tid; + + LIST_HEAD(sd_resolve_query, queries); +}; + +struct sd_resolve_query { + unsigned n_ref; + + sd_resolve *resolve; + + QueryType type:4; + bool done:1; + bool floating:1; + unsigned id; + + int ret; + int _errno; + int _h_errno; + struct addrinfo *addrinfo; + char *serv, *host; + + union { + sd_resolve_getaddrinfo_handler_t getaddrinfo_handler; + sd_resolve_getnameinfo_handler_t getnameinfo_handler; + }; + + void *userdata; + + LIST_FIELDS(sd_resolve_query, queries); +}; + +typedef struct RHeader { + QueryType type; + unsigned id; + size_t length; +} RHeader; + +typedef struct AddrInfoRequest { + struct RHeader header; + bool hints_valid; + 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; + bool gethost:1, getserv:1; +} NameInfoRequest; + +typedef struct NameInfoResponse { + struct RHeader header; + size_t hostlen, servlen; + int ret; + int _errno; + int _h_errno; +} NameInfoResponse; + +typedef union Packet { + RHeader rheader; + AddrInfoRequest addrinfo_request; + AddrInfoResponse addrinfo_response; + NameInfoRequest nameinfo_request; + NameInfoResponse nameinfo_response; +} Packet; + +static int getaddrinfo_done(sd_resolve_query* q); +static int getnameinfo_done(sd_resolve_query *q); + +static void resolve_query_disconnect(sd_resolve_query *q); + +#define RESOLVE_DONT_DESTROY(resolve) \ + _cleanup_(sd_resolve_unrefp) _unused_ sd_resolve *_dont_destroy_##resolve = sd_resolve_ref(resolve) + +static int send_died(int out_fd) { + + RHeader rh = { + .type = RESPONSE_DIED, + .length = sizeof(RHeader), + }; + + assert(out_fd >= 0); + + if (send(out_fd, &rh, rh.length, MSG_NOSIGNAL) < 0) + return -errno; + + return 0; +} + +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); + memcpy_safe((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, + ai->ai_canonname, cnl); + + *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 resp = { + .header.type = RESPONSE_ADDRINFO, + .header.id = id, + .header.length = sizeof(AddrInfoResponse), + .ret = ret, + ._errno = _errno, + ._h_errno = _h_errno, + }; + + struct msghdr mh = {}; + struct iovec iov[2]; + union { + AddrInfoSerialization ais; + uint8_t space[BUFSIZE]; + } buffer; + + assert(out_fd >= 0); + + if (ret == 0 && ai) { + void *p = &buffer; + struct addrinfo *k; + + for (k = ai; k; k = k->ai_next) { + p = serialize_addrinfo(p, k, &resp.header.length, (uint8_t*) &buffer + BUFSIZE - (uint8_t*) p); + if (!p) { + freeaddrinfo(ai); + return -ENOBUFS; + } + } + } + + if (ai) + freeaddrinfo(ai); + + iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(AddrInfoResponse) }; + iov[1] = (struct iovec) { .iov_base = &buffer, .iov_len = resp.header.length - sizeof(AddrInfoResponse) }; + + mh.msg_iov = iov; + mh.msg_iovlen = ELEMENTSOF(iov); + + if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) + return -errno; + + return 0; +} + +static int send_nameinfo_reply( + int out_fd, + unsigned id, + int ret, + const char *host, + const char *serv, + int _errno, + int _h_errno) { + + NameInfoResponse resp = { + .header.type = RESPONSE_NAMEINFO, + .header.id = id, + .ret = ret, + ._errno = _errno, + ._h_errno = _h_errno, + }; + + struct msghdr mh = {}; + struct iovec iov[3]; + size_t hl, sl; + + assert(out_fd >= 0); + + sl = serv ? strlen(serv)+1 : 0; + hl = host ? strlen(host)+1 : 0; + + resp.header.length = sizeof(NameInfoResponse) + hl + sl; + resp.hostlen = hl; + resp.servlen = sl; + + iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(NameInfoResponse) }; + iov[1] = (struct iovec) { .iov_base = (void*) host, .iov_len = hl }; + iov[2] = (struct iovec) { .iov_base = (void*) serv, .iov_len = sl }; + + mh.msg_iov = iov; + mh.msg_iovlen = ELEMENTSOF(iov); + + if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) + return -errno; + + return 0; +} + +static int handle_request(int out_fd, const Packet *packet, size_t length) { + const RHeader *req; + + assert(out_fd >= 0); + assert(packet); + + req = &packet->rheader; + + assert(length >= sizeof(RHeader)); + assert(length == req->length); + + switch (req->type) { + + case REQUEST_ADDRINFO: { + const AddrInfoRequest *ai_req = &packet->addrinfo_request; + struct addrinfo hints = {}, *result = NULL; + const char *node, *service; + int ret; + + assert(length >= sizeof(AddrInfoRequest)); + assert(length == sizeof(AddrInfoRequest) + ai_req->node_len + ai_req->service_len); + + hints.ai_flags = ai_req->ai_flags; + hints.ai_family = ai_req->ai_family; + hints.ai_socktype = ai_req->ai_socktype; + hints.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_valid ? &hints : NULL, + &result); + + /* send_addrinfo_reply() frees result */ + return send_addrinfo_reply(out_fd, req->id, ret, result, errno, h_errno); + } + + case REQUEST_NAMEINFO: { + const NameInfoRequest *ni_req = &packet->nameinfo_request; + char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV]; + union sockaddr_union sa; + int ret; + + assert(length >= sizeof(NameInfoRequest)); + assert(length == sizeof(NameInfoRequest) + ni_req->sockaddr_len); + assert(sizeof(sa) >= ni_req->sockaddr_len); + + memcpy(&sa, (const uint8_t *) ni_req + sizeof(NameInfoRequest), ni_req->sockaddr_len); + + ret = getnameinfo(&sa.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_TERMINATE: + /* Quit */ + return -ECONNRESET; + + default: + assert_not_reached("Unknown request"); + } + + return 0; +} + +static void* thread_worker(void *p) { + sd_resolve *resolve = p; + sigset_t fullset; + + /* No signals in this thread please */ + assert_se(sigfillset(&fullset) == 0); + assert_se(pthread_sigmask(SIG_BLOCK, &fullset, NULL) == 0); + + /* Assign a pretty name to this thread */ + (void) prctl(PR_SET_NAME, (unsigned long) "sd-resolve"); + + while (!resolve->dead) { + union { + Packet packet; + uint8_t space[BUFSIZE]; + } buf; + ssize_t length; + + length = recv(resolve->fds[REQUEST_RECV_FD], &buf, sizeof(buf), 0); + if (length < 0) { + if (errno == EINTR) + continue; + + break; + } + if (length == 0) + break; + + if (resolve->dead) + break; + + if (handle_request(resolve->fds[RESPONSE_SEND_FD], &buf.packet, (size_t) length) < 0) + break; + } + + send_died(resolve->fds[RESPONSE_SEND_FD]); + + return NULL; +} + +static int start_threads(sd_resolve *resolve, unsigned extra) { + unsigned n; + int r; + + n = resolve->n_outstanding + extra; + n = CLAMP(n, WORKERS_MIN, WORKERS_MAX); + + while (resolve->n_valid_workers < n) { + + r = pthread_create(&resolve->workers[resolve->n_valid_workers], NULL, thread_worker, resolve); + if (r != 0) + return -r; + + resolve->n_valid_workers++; + } + + return 0; +} + +static bool resolve_pid_changed(sd_resolve *r) { + assert(r); + + /* We don't support people creating a resolver and keeping it + * around after fork(). Let's complain. */ + + return r->original_pid != getpid(); +} + +_public_ int sd_resolve_new(sd_resolve **ret) { + sd_resolve *resolve = NULL; + int i, r; + + assert_return(ret, -EINVAL); + + resolve = new0(sd_resolve, 1); + if (!resolve) + return -ENOMEM; + + resolve->n_ref = 1; + resolve->original_pid = getpid(); + + for (i = 0; i < _FD_MAX; i++) + resolve->fds[i] = -1; + + r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + REQUEST_RECV_FD); + if (r < 0) { + r = -errno; + goto fail; + } + + r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + RESPONSE_RECV_FD); + if (r < 0) { + r = -errno; + goto fail; + } + + fd_inc_sndbuf(resolve->fds[REQUEST_SEND_FD], QUERIES_MAX * BUFSIZE); + fd_inc_rcvbuf(resolve->fds[REQUEST_RECV_FD], QUERIES_MAX * BUFSIZE); + fd_inc_sndbuf(resolve->fds[RESPONSE_SEND_FD], QUERIES_MAX * BUFSIZE); + fd_inc_rcvbuf(resolve->fds[RESPONSE_RECV_FD], QUERIES_MAX * BUFSIZE); + + fd_nonblock(resolve->fds[RESPONSE_RECV_FD], true); + + *ret = resolve; + return 0; + +fail: + sd_resolve_unref(resolve); + return r; +} + +_public_ int sd_resolve_default(sd_resolve **ret) { + + static thread_local sd_resolve *default_resolve = NULL; + sd_resolve *e = NULL; + int r; + + if (!ret) + return !!default_resolve; + + if (default_resolve) { + *ret = sd_resolve_ref(default_resolve); + return 0; + } + + r = sd_resolve_new(&e); + if (r < 0) + return r; + + e->default_resolve_ptr = &default_resolve; + e->tid = gettid(); + default_resolve = e; + + *ret = e; + return 1; +} + +_public_ int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid) { + assert_return(resolve, -EINVAL); + assert_return(tid, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + if (resolve->tid != 0) { + *tid = resolve->tid; + return 0; + } + + if (resolve->event) + return sd_event_get_tid(resolve->event, tid); + + return -ENXIO; +} + +static void resolve_free(sd_resolve *resolve) { + PROTECT_ERRNO; + sd_resolve_query *q; + unsigned i; + + assert(resolve); + + while ((q = resolve->queries)) { + assert(q->floating); + resolve_query_disconnect(q); + sd_resolve_query_unref(q); + } + + if (resolve->default_resolve_ptr) + *(resolve->default_resolve_ptr) = NULL; + + resolve->dead = true; + + sd_resolve_detach_event(resolve); + + if (resolve->fds[REQUEST_SEND_FD] >= 0) { + + RHeader req = { + .type = REQUEST_TERMINATE, + .length = sizeof(req) + }; + + /* Send one termination packet for each worker */ + for (i = 0; i < resolve->n_valid_workers; i++) + (void) send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL); + } + + /* Now terminate them and wait until they are gone. + If we get an error than most likely the thread already exited. */ + for (i = 0; i < resolve->n_valid_workers; i++) + (void) pthread_join(resolve->workers[i], NULL); + + /* Close all communication channels */ + for (i = 0; i < _FD_MAX; i++) + safe_close(resolve->fds[i]); + + free(resolve); +} + +_public_ sd_resolve* sd_resolve_ref(sd_resolve *resolve) { + assert_return(resolve, NULL); + + assert(resolve->n_ref >= 1); + resolve->n_ref++; + + return resolve; +} + +_public_ sd_resolve* sd_resolve_unref(sd_resolve *resolve) { + + if (!resolve) + return NULL; + + assert(resolve->n_ref >= 1); + resolve->n_ref--; + + if (resolve->n_ref <= 0) + resolve_free(resolve); + + return NULL; +} + +_public_ int sd_resolve_get_fd(sd_resolve *resolve) { + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + return resolve->fds[RESPONSE_RECV_FD]; +} + +_public_ int sd_resolve_get_events(sd_resolve *resolve) { + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + return resolve->n_queries > resolve->n_done ? POLLIN : 0; +} + +_public_ int sd_resolve_get_timeout(sd_resolve *resolve, uint64_t *usec) { + assert_return(resolve, -EINVAL); + assert_return(usec, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + *usec = (uint64_t) -1; + return 0; +} + +static sd_resolve_query *lookup_query(sd_resolve *resolve, unsigned id) { + sd_resolve_query *q; + + assert(resolve); + + q = resolve->query_array[id % QUERIES_MAX]; + if (q) + if (q->id == id) + return q; + + return NULL; +} + +static int complete_query(sd_resolve *resolve, sd_resolve_query *q) { + int r; + + assert(q); + assert(!q->done); + assert(q->resolve == resolve); + + q->done = true; + resolve->n_done++; + + resolve->current = sd_resolve_query_ref(q); + + switch (q->type) { + + case REQUEST_ADDRINFO: + r = getaddrinfo_done(q); + break; + + case REQUEST_NAMEINFO: + r = getnameinfo_done(q); + break; + + default: + assert_not_reached("Cannot complete unknown query type"); + } + + resolve->current = NULL; + + if (q->floating) { + resolve_query_disconnect(q); + sd_resolve_query_unref(q); + } + + sd_resolve_query_unref(q); + + return r; +} + +static int unserialize_addrinfo(const void **p, size_t *length, struct addrinfo **ret_ai) { + AddrInfoSerialization s; + size_t l; + struct addrinfo *ai; + + assert(p); + assert(*p); + assert(ret_ai); + assert(length); + + if (*length < sizeof(AddrInfoSerialization)) + return -EBADMSG; + + memcpy(&s, *p, sizeof(s)); + + l = sizeof(AddrInfoSerialization) + s.ai_addrlen + s.canonname_len; + if (*length < l) + return -EBADMSG; + + ai = new0(struct addrinfo, 1); + if (!ai) + return -ENOMEM; + + 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 (s.ai_addrlen > 0) { + ai->ai_addr = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization), s.ai_addrlen); + if (!ai->ai_addr) { + free(ai); + return -ENOMEM; + } + } + + if (s.canonname_len > 0) { + ai->ai_canonname = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization) + s.ai_addrlen, s.canonname_len); + if (!ai->ai_canonname) { + free(ai->ai_addr); + free(ai); + return -ENOMEM; + } + } + + *length -= l; + *ret_ai = ai; + *p = ((const uint8_t*) *p) + l; + + return 0; +} + +static int handle_response(sd_resolve *resolve, const Packet *packet, size_t length) { + const RHeader *resp; + sd_resolve_query *q; + int r; + + assert(resolve); + + resp = &packet->rheader; + assert(resp); + assert(length >= sizeof(RHeader)); + assert(length == resp->length); + + if (resp->type == RESPONSE_DIED) { + resolve->dead = true; + return 0; + } + + assert(resolve->n_outstanding > 0); + resolve->n_outstanding--; + + 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; + + r = unserialize_addrinfo(&p, &l, &ai); + if (r < 0) { + q->ret = EAI_SYSTEM; + q->_errno = -r; + q->_h_errno = 0; + freeaddrinfo(q->addrinfo); + q->addrinfo = NULL; + break; + } + + if (prev) + prev->ai_next = ai; + else + q->addrinfo = ai; + + prev = ai; + } + + return complete_query(resolve, q); + } + + 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 > 0) { + q->host = strndup((const char*) ni_resp + sizeof(NameInfoResponse), ni_resp->hostlen-1); + if (!q->host) { + q->ret = EAI_MEMORY; + q->_errno = ENOMEM; + q->_h_errno = 0; + } + } + + if (ni_resp->servlen > 0) { + q->serv = strndup((const char*) ni_resp + sizeof(NameInfoResponse) + ni_resp->hostlen, ni_resp->servlen-1); + if (!q->serv) { + q->ret = EAI_MEMORY; + q->_errno = ENOMEM; + q->_h_errno = 0; + } + } + + return complete_query(resolve, q); + } + + default: + return 0; + } +} + +_public_ int sd_resolve_process(sd_resolve *resolve) { + RESOLVE_DONT_DESTROY(resolve); + + union { + Packet packet; + uint8_t space[BUFSIZE]; + } buf; + ssize_t l; + int r; + + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + /* We don't allow recursively invoking sd_resolve_process(). */ + assert_return(!resolve->current, -EBUSY); + + l = recv(resolve->fds[RESPONSE_RECV_FD], &buf, sizeof(buf), 0); + if (l < 0) { + if (errno == EAGAIN) + return 0; + + return -errno; + } + if (l == 0) + return -ECONNREFUSED; + + r = handle_response(resolve, &buf.packet, (size_t) l); + if (r < 0) + return r; + + return 1; +} + +_public_ int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec) { + int r; + + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + if (resolve->n_done >= resolve->n_queries) + return 0; + + do { + r = fd_wait_for_event(resolve->fds[RESPONSE_RECV_FD], POLLIN, timeout_usec); + } while (r == -EINTR); + + if (r < 0) + return r; + + return sd_resolve_process(resolve); +} + +static int alloc_query(sd_resolve *resolve, bool floating, sd_resolve_query **_q) { + sd_resolve_query *q; + int r; + + assert(resolve); + assert(_q); + + if (resolve->n_queries >= QUERIES_MAX) + return -ENOBUFS; + + r = start_threads(resolve, 1); + if (r < 0) + return r; + + while (resolve->query_array[resolve->current_id % QUERIES_MAX]) + resolve->current_id++; + + q = resolve->query_array[resolve->current_id % QUERIES_MAX] = new0(sd_resolve_query, 1); + if (!q) + return -ENOMEM; + + q->n_ref = 1; + q->resolve = resolve; + q->floating = floating; + q->id = resolve->current_id++; + + if (!floating) + sd_resolve_ref(resolve); + + LIST_PREPEND(queries, resolve->queries, q); + resolve->n_queries++; + + *_q = q; + return 0; +} + +_public_ int sd_resolve_getaddrinfo( + sd_resolve *resolve, + sd_resolve_query **_q, + const char *node, const char *service, + const struct addrinfo *hints, + sd_resolve_getaddrinfo_handler_t callback, void *userdata) { + + AddrInfoRequest req = {}; + struct msghdr mh = {}; + struct iovec iov[3]; + sd_resolve_query *q; + int r; + + assert_return(resolve, -EINVAL); + assert_return(node || service, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + r = alloc_query(resolve, !_q, &q); + if (r < 0) + return r; + + q->type = REQUEST_ADDRINFO; + q->getaddrinfo_handler = callback; + q->userdata = userdata; + + req.node_len = node ? strlen(node)+1 : 0; + req.service_len = service ? strlen(service)+1 : 0; + + req.header.id = q->id; + req.header.type = REQUEST_ADDRINFO; + req.header.length = sizeof(AddrInfoRequest) + req.node_len + req.service_len; + + if (hints) { + req.hints_valid = true; + req.ai_flags = hints->ai_flags; + req.ai_family = hints->ai_family; + req.ai_socktype = hints->ai_socktype; + req.ai_protocol = hints->ai_protocol; + } + + iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(AddrInfoRequest) }; + if (node) + iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) node, .iov_len = req.node_len }; + if (service) + iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) service, .iov_len = req.service_len }; + mh.msg_iov = iov; + + if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { + sd_resolve_query_unref(q); + return -errno; + } + + resolve->n_outstanding++; + + if (_q) + *_q = q; + + return 0; +} + +static int getaddrinfo_done(sd_resolve_query* q) { + assert(q); + assert(q->done); + assert(q->getaddrinfo_handler); + + errno = q->_errno; + h_errno = q->_h_errno; + + return q->getaddrinfo_handler(q, q->ret, q->addrinfo, q->userdata); +} + +_public_ int sd_resolve_getnameinfo( + sd_resolve *resolve, + sd_resolve_query**_q, + const struct sockaddr *sa, socklen_t salen, + int flags, + uint64_t get, + sd_resolve_getnameinfo_handler_t callback, + void *userdata) { + + NameInfoRequest req = {}; + struct msghdr mh = {}; + struct iovec iov[2]; + sd_resolve_query *q; + int r; + + assert_return(resolve, -EINVAL); + assert_return(sa, -EINVAL); + assert_return(salen >= sizeof(struct sockaddr), -EINVAL); + assert_return(salen <= sizeof(union sockaddr_union), -EINVAL); + assert_return((get & ~SD_RESOLVE_GET_BOTH) == 0, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + r = alloc_query(resolve, !_q, &q); + if (r < 0) + return r; + + q->type = REQUEST_NAMEINFO; + q->getnameinfo_handler = callback; + q->userdata = userdata; + + req.header.id = q->id; + req.header.type = REQUEST_NAMEINFO; + req.header.length = sizeof(NameInfoRequest) + salen; + + req.flags = flags; + req.sockaddr_len = salen; + req.gethost = !!(get & SD_RESOLVE_GET_HOST); + req.getserv = !!(get & SD_RESOLVE_GET_SERVICE); + + iov[0] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(NameInfoRequest) }; + iov[1] = (struct iovec) { .iov_base = (void*) sa, .iov_len = salen }; + + mh.msg_iov = iov; + mh.msg_iovlen = 2; + + if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { + sd_resolve_query_unref(q); + return -errno; + } + + resolve->n_outstanding++; + + if (_q) + *_q = q; + + return 0; +} + +static int getnameinfo_done(sd_resolve_query *q) { + + assert(q); + assert(q->done); + assert(q->getnameinfo_handler); + + errno = q->_errno; + h_errno= q->_h_errno; + + return q->getnameinfo_handler(q, q->ret, q->host, q->serv, q->userdata); +} + +_public_ sd_resolve_query* sd_resolve_query_ref(sd_resolve_query *q) { + assert_return(q, NULL); + + assert(q->n_ref >= 1); + q->n_ref++; + + return q; +} + +static void resolve_freeaddrinfo(struct addrinfo *ai) { + while (ai) { + struct addrinfo *next = ai->ai_next; + + free(ai->ai_addr); + free(ai->ai_canonname); + free(ai); + ai = next; + } +} + +static void resolve_query_disconnect(sd_resolve_query *q) { + sd_resolve *resolve; + unsigned i; + + assert(q); + + if (!q->resolve) + return; + + resolve = q->resolve; + assert(resolve->n_queries > 0); + + if (q->done) { + assert(resolve->n_done > 0); + resolve->n_done--; + } + + i = q->id % QUERIES_MAX; + assert(resolve->query_array[i] == q); + resolve->query_array[i] = NULL; + LIST_REMOVE(queries, resolve->queries, q); + resolve->n_queries--; + + q->resolve = NULL; + if (!q->floating) + sd_resolve_unref(resolve); +} + +static void resolve_query_free(sd_resolve_query *q) { + assert(q); + + resolve_query_disconnect(q); + + resolve_freeaddrinfo(q->addrinfo); + free(q->host); + free(q->serv); + free(q); +} + +_public_ sd_resolve_query* sd_resolve_query_unref(sd_resolve_query* q) { + if (!q) + return NULL; + + assert(q->n_ref >= 1); + q->n_ref--; + + if (q->n_ref <= 0) + resolve_query_free(q); + + return NULL; +} + +_public_ int sd_resolve_query_is_done(sd_resolve_query *q) { + assert_return(q, -EINVAL); + assert_return(!resolve_pid_changed(q->resolve), -ECHILD); + + return q->done; +} + +_public_ void* sd_resolve_query_set_userdata(sd_resolve_query *q, void *userdata) { + void *ret; + + assert_return(q, NULL); + assert_return(!resolve_pid_changed(q->resolve), NULL); + + ret = q->userdata; + q->userdata = userdata; + + return ret; +} + +_public_ void* sd_resolve_query_get_userdata(sd_resolve_query *q) { + assert_return(q, NULL); + assert_return(!resolve_pid_changed(q->resolve), NULL); + + return q->userdata; +} + +_public_ sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q) { + assert_return(q, NULL); + assert_return(!resolve_pid_changed(q->resolve), NULL); + + return q->resolve; +} + +static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + sd_resolve *resolve = userdata; + int r; + + assert(resolve); + + r = sd_resolve_process(resolve); + if (r < 0) + return r; + + return 1; +} + +_public_ int sd_resolve_attach_event(sd_resolve *resolve, sd_event *event, int64_t priority) { + int r; + + assert_return(resolve, -EINVAL); + assert_return(!resolve->event, -EBUSY); + + assert(!resolve->event_source); + + if (event) + resolve->event = sd_event_ref(event); + else { + r = sd_event_default(&resolve->event); + if (r < 0) + return r; + } + + r = sd_event_add_io(resolve->event, &resolve->event_source, resolve->fds[RESPONSE_RECV_FD], POLLIN, io_callback, resolve); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(resolve->event_source, priority); + if (r < 0) + goto fail; + + return 0; + +fail: + sd_resolve_detach_event(resolve); + return r; +} + +_public_ int sd_resolve_detach_event(sd_resolve *resolve) { + assert_return(resolve, -EINVAL); + + if (!resolve->event) + return 0; + + if (resolve->event_source) { + sd_event_source_set_enabled(resolve->event_source, SD_EVENT_OFF); + resolve->event_source = sd_event_source_unref(resolve->event_source); + } + + resolve->event = sd_event_unref(resolve->event); + return 1; +} + +_public_ sd_event *sd_resolve_get_event(sd_resolve *resolve) { + assert_return(resolve, NULL); + + return resolve->event; +} diff --git a/src/libsystemd/src/sd-resolve/test-resolve.c b/src/libsystemd/src/sd-resolve/test-resolve.c new file mode 100644 index 0000000000..0209cc77e7 --- /dev/null +++ b/src/libsystemd/src/sd-resolve/test-resolve.c @@ -0,0 +1,114 @@ +/*** + This file is part of systemd. + + Copyright 2005-2008 Lennart Poettering + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "alloc-util.h" +#include "macro.h" +#include "socket-util.h" +#include "string-util.h" + +static int getaddrinfo_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) { + const struct addrinfo *i; + + assert_se(q); + + if (ret != 0) { + log_error("getaddrinfo error: %s %i", gai_strerror(ret), ret); + return 0; + } + + for (i = ai; i; i = i->ai_next) { + _cleanup_free_ char *addr = NULL; + + assert_se(sockaddr_pretty(i->ai_addr, i->ai_addrlen, false, true, &addr) == 0); + puts(addr); + } + + printf("canonical name: %s\n", strna(ai->ai_canonname)); + + return 0; +} + +static int getnameinfo_handler(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata) { + assert_se(q); + + if (ret != 0) { + log_error("getnameinfo error: %s %i", gai_strerror(ret), ret); + return 0; + } + + printf("Host: %s — Serv: %s\n", strna(host), strna(serv)); + return 0; +} + +int main(int argc, char *argv[]) { + _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q1 = NULL, *q2 = NULL; + _cleanup_(sd_resolve_unrefp) sd_resolve *resolve = NULL; + int r = 0; + + struct addrinfo hints = { + .ai_family = PF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_CANONNAME + }; + + struct sockaddr_in sa = { + .sin_family = AF_INET, + .sin_port = htons(80) + }; + + assert_se(sd_resolve_default(&resolve) >= 0); + + /* Test a floating resolver query */ + sd_resolve_getaddrinfo(resolve, NULL, "redhat.com", "http", NULL, getaddrinfo_handler, NULL); + + /* Make a name -> address query */ + r = sd_resolve_getaddrinfo(resolve, &q1, argc >= 2 ? argv[1] : "www.heise.de", NULL, &hints, getaddrinfo_handler, NULL); + if (r < 0) + log_error_errno(r, "sd_resolve_getaddrinfo(): %m"); + + /* Make an address -> name query */ + sa.sin_addr.s_addr = inet_addr(argc >= 3 ? argv[2] : "193.99.144.71"); + r = sd_resolve_getnameinfo(resolve, &q2, (struct sockaddr*) &sa, sizeof(sa), 0, SD_RESOLVE_GET_BOTH, getnameinfo_handler, NULL); + if (r < 0) + log_error_errno(r, "sd_resolve_getnameinfo(): %m"); + + /* Wait until all queries are completed */ + for (;;) { + r = sd_resolve_wait(resolve, (uint64_t) -1); + if (r == 0) + break; + if (r < 0) { + log_error_errno(r, "sd_resolve_wait(): %m"); + assert_not_reached("sd_resolve_wait() failed"); + } + } + + return 0; +} diff --git a/src/libsystemd/src/sd-utf8/sd-utf8.c b/src/libsystemd/src/sd-utf8/sd-utf8.c new file mode 100644 index 0000000000..77be8e1996 --- /dev/null +++ b/src/libsystemd/src/sd-utf8/sd-utf8.c @@ -0,0 +1,35 @@ +/*** + 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 . +***/ + +#include + +#include "utf8.h" +#include "util.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/src/subdir.mk b/src/libsystemd/src/subdir.mk new file mode 100644 index 0000000000..35def00fdc --- /dev/null +++ b/src/libsystemd/src/subdir.mk @@ -0,0 +1,29 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# 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 . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +systemd.CPPFLAGS += -DLIBDIR=\"$(libdir)\" +systemd.CPPFLAGS += -DUDEVLIBEXECDIR=\"$(udevlibexecdir)\" + +include $(topsrcdir)/build-aux/Makefile.tail.mk -- cgit v1.2.3-54-g00ecf