summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2018-01-28 22:49:17 +0100
committerMichael Biebl <biebl@debian.org>2018-01-28 22:49:17 +0100
commit1d42b86df9052528a8f56b2f52d8bc2faf87b2da (patch)
tree0d80f37a1ad6f02067261ee3e7ee62e1869fcd56 /src
parent52ad194e0b816b8273dd8d0fea3e6d467f6ca34e (diff)
downloadsystemd-1d42b86df9052528a8f56b2f52d8bc2faf87b2da.tar.gz
New upstream version 237
Diffstat (limited to 'src')
-rw-r--r--src/activate/activate.c31
-rw-r--r--src/analyze/analyze-verify.c2
-rw-r--r--src/analyze/analyze.c331
-rw-r--r--src/basic/af-list.h2
-rw-r--r--src/basic/async.c69
-rw-r--r--src/basic/async.h2
-rw-r--r--src/basic/blockdev-util.c199
-rw-r--r--src/basic/blockdev-util.h (renamed from src/libsystemd/sd-bus/bus-bloom.h)32
-rw-r--r--src/basic/btrfs-ctree.h1
-rw-r--r--src/basic/btrfs-util.c20
-rw-r--r--src/basic/btrfs-util.h1
-rw-r--r--src/basic/build.h7
-rw-r--r--src/basic/calendarspec.c29
-rw-r--r--src/basic/cgroup-util.c12
-rw-r--r--src/basic/def.h7
-rw-r--r--src/basic/device-nodes.h5
-rw-r--r--src/basic/errno-list.c1
-rw-r--r--src/basic/errno-list.h4
-rw-r--r--src/basic/ether-addr-util.c1
-rw-r--r--src/basic/exec-util.c44
-rw-r--r--src/basic/fd-util.c23
-rw-r--r--src/basic/fileio.c39
-rw-r--r--src/basic/fs-util.c136
-rw-r--r--src/basic/fs-util.h12
-rw-r--r--src/basic/gcrypt-util.c (renamed from src/shared/gcrypt-util.c)0
-rw-r--r--src/basic/gcrypt-util.h (renamed from src/shared/gcrypt-util.h)0
-rwxr-xr-xsrc/basic/generate-af-list.sh3
-rwxr-xr-xsrc/basic/generate-arphrd-list.sh3
-rwxr-xr-xsrc/basic/generate-cap-list.sh3
-rwxr-xr-xsrc/basic/generate-errno-list.sh3
-rwxr-xr-xsrc/basic/generate-socket-protocol-list.sh6
-rw-r--r--src/basic/gunicode.c2
-rw-r--r--src/basic/hash-funcs.c2
-rw-r--r--src/basic/hostname-util.h1
-rw-r--r--src/basic/io-util.c9
-rw-r--r--src/basic/journal-importer.c1
-rw-r--r--src/basic/label.c25
-rw-r--r--src/basic/label.h2
-rw-r--r--src/basic/locale-util.h8
-rw-r--r--src/basic/log.c90
-rw-r--r--src/basic/log.h61
-rw-r--r--src/basic/macro.h10
-rw-r--r--src/basic/meson.build38
-rw-r--r--src/basic/missing.h76
-rw-r--r--src/basic/missing_syscall.h4
-rw-r--r--src/basic/mkdir-label.c21
-rw-r--r--src/basic/mkdir.c28
-rw-r--r--src/basic/mkdir.h1
-rw-r--r--src/basic/parse-util.c40
-rw-r--r--src/basic/path-util.c37
-rw-r--r--src/basic/path-util.h1
-rw-r--r--src/basic/process-util.c294
-rw-r--r--src/basic/process-util.h67
-rw-r--r--src/basic/procfs-util.c138
-rw-r--r--src/basic/procfs-util.h8
-rw-r--r--src/basic/random-util.c5
-rw-r--r--src/basic/raw-clone.h42
-rw-r--r--src/basic/rm-rf.h3
-rw-r--r--src/basic/securebits-util.c1
-rw-r--r--src/basic/securebits-util.h8
-rw-r--r--src/basic/signal-util.h7
-rw-r--r--src/basic/smack-util.c2
-rw-r--r--src/basic/socket-label.c23
-rw-r--r--src/basic/socket-protocol-list.c57
-rw-r--r--src/basic/socket-protocol-list.h26
-rw-r--r--src/basic/socket-protocol-to-name.awk9
-rw-r--r--src/basic/socket-util.c146
-rw-r--r--src/basic/socket-util.h17
-rw-r--r--src/basic/stat-util.c5
-rw-r--r--src/basic/string-util.c29
-rw-r--r--src/basic/string-util.h8
-rw-r--r--src/basic/terminal-util.c49
-rw-r--r--src/basic/time-util.c19
-rw-r--r--src/basic/unaligned.h60
-rw-r--r--src/basic/unit-name.c9
-rw-r--r--src/basic/user-util.c44
-rw-r--r--src/basic/user-util.h2
-rw-r--r--src/basic/util.c329
-rw-r--r--src/basic/util.h9
-rw-r--r--src/basic/verbs.c44
-rw-r--r--src/basic/verbs.h8
-rw-r--r--src/basic/virt.c47
-rw-r--r--src/boot/bootctl.c23
-rw-r--r--src/boot/efi/boot.c2
-rw-r--r--src/boot/efi/measure.c5
-rwxr-xr-xsrc/boot/efi/no-undefined-symbols.sh3
-rw-r--r--src/boot/efi/stub.c24
-rw-r--r--src/busctl/busctl.c28
-rw-r--r--src/cgls/cgls.c6
-rw-r--r--src/cgtop/cgtop.c35
-rw-r--r--src/core/automount.c2
-rw-r--r--src/core/cgroup.c141
-rw-r--r--src/core/cgroup.h4
-rw-r--r--src/core/dbus-automount.c25
-rw-r--r--src/core/dbus-cgroup.c496
-rw-r--r--src/core/dbus-execute.c1046
-rw-r--r--src/core/dbus-execute.h1
-rw-r--r--src/core/dbus-kill.c80
-rw-r--r--src/core/dbus-manager.c7
-rw-r--r--src/core/dbus-mount.c46
-rw-r--r--src/core/dbus-path.c109
-rw-r--r--src/core/dbus-path.h4
-rw-r--r--src/core/dbus-scope.c19
-rw-r--r--src/core/dbus-service.c410
-rw-r--r--src/core/dbus-socket.c315
-rw-r--r--src/core/dbus-timer.c204
-rw-r--r--src/core/dbus-unit.c264
-rw-r--r--src/core/dbus-util.c119
-rw-r--r--src/core/dbus-util.h351
-rw-r--r--src/core/dbus.c113
-rw-r--r--src/core/device.c10
-rw-r--r--src/core/emergency-action.c5
-rw-r--r--src/core/execute.c61
-rw-r--r--src/core/ip-address-access.c13
-rw-r--r--src/core/job.c5
-rw-r--r--src/core/kill.c3
-rw-r--r--src/core/killall.c8
-rw-r--r--src/core/killall.h4
-rw-r--r--src/core/load-fragment-gperf.gperf.m45
-rw-r--r--src/core/load-fragment.c159
-rw-r--r--src/core/locale-setup.c1
-rw-r--r--src/core/loopback-setup.c25
-rw-r--r--src/core/machine-id-setup.c7
-rw-r--r--src/core/main.c600
-rw-r--r--src/core/manager.c808
-rw-r--r--src/core/manager.h39
-rw-r--r--src/core/meson.build2
-rw-r--r--src/core/mount-setup.c15
-rw-r--r--src/core/mount.c60
-rw-r--r--src/core/mount.h3
-rw-r--r--src/core/namespace.c118
-rw-r--r--src/core/namespace.h2
-rw-r--r--src/core/path.c6
-rw-r--r--src/core/scope.c15
-rw-r--r--src/core/service.c226
-rw-r--r--src/core/shutdown.c57
-rw-r--r--src/core/slice.c22
-rw-r--r--src/core/socket.c73
-rw-r--r--src/core/socket.h5
-rw-r--r--src/core/swap.c2
-rw-r--r--src/core/timer.c2
-rw-r--r--src/core/umount.c22
-rw-r--r--src/core/unit.c204
-rw-r--r--src/core/unit.h18
-rw-r--r--src/coredump/coredump.c2
-rw-r--r--src/coredump/coredumpctl.c25
-rw-r--r--src/cryptsetup/cryptsetup-generator.c44
-rw-r--r--src/cryptsetup/cryptsetup.c13
-rw-r--r--src/debug-generator/debug-generator.c3
-rw-r--r--src/delta/delta.c69
-rw-r--r--src/firstboot/firstboot.c15
-rw-r--r--src/fsck/fsck.c45
-rw-r--r--src/fstab-generator/fstab-generator.c57
-rw-r--r--src/fuzz/fuzz-dhcp-server.c68
-rw-r--r--src/fuzz/fuzz-dns-packet.c40
-rw-r--r--src/fuzz/fuzz-dns-packet.options2
-rw-r--r--src/fuzz/fuzz-dns-server.options2
-rw-r--r--src/fuzz/fuzz-main.c51
-rw-r--r--src/fuzz/fuzz.h23
-rw-r--r--src/fuzz/meson.build30
-rw-r--r--src/getty-generator/getty-generator.c3
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c4
-rw-r--r--src/hibernate-resume/hibernate-resume-generator.c3
-rw-r--r--src/hostname/hostnamed.c4
-rw-r--r--src/import/curl-util.c4
-rw-r--r--src/import/export.c1
-rw-r--r--src/import/import-common.c27
-rw-r--r--src/import/import-compress.c2
-rw-r--r--src/import/import-tar.c2
-rw-r--r--src/import/import.c1
-rw-r--r--src/import/importd.c16
-rw-r--r--src/import/pull-common.c16
-rw-r--r--src/import/pull-tar.c4
-rw-r--r--src/import/pull.c1
-rw-r--r--src/initctl/initctl.c1
-rw-r--r--src/journal-remote/journal-gatewayd.c3
-rw-r--r--src/journal-remote/journal-remote.c27
-rw-r--r--src/journal-remote/journal-upload.c3
-rwxr-xr-xsrc/journal/generate-audit_type-list.sh3
-rw-r--r--src/journal/journal-file.c23
-rw-r--r--src/journal/journal-send.c5
-rw-r--r--src/journal/journalctl.c278
-rw-r--r--src/journal/journald-native.c2
-rw-r--r--src/journal/journald-server.c27
-rw-r--r--src/journal/journald.c4
-rw-r--r--src/journal/meson.build14
-rw-r--r--src/journal/sd-journal.c1
-rw-r--r--src/journal/test-compress-benchmark.c1
-rw-r--r--src/libsystemd-network/arp-util.c5
-rw-r--r--src/libsystemd-network/dhcp-lease-internal.h2
-rw-r--r--src/libsystemd-network/dhcp-network.c5
-rw-r--r--src/libsystemd-network/dhcp6-internal.h65
-rw-r--r--src/libsystemd-network/dhcp6-lease-internal.h2
-rw-r--r--src/libsystemd-network/dhcp6-option.c316
-rw-r--r--src/libsystemd-network/dhcp6-protocol.h1
-rw-r--r--src/libsystemd-network/network-internal.c9
-rw-r--r--src/libsystemd-network/network-internal.h3
-rw-r--r--src/libsystemd-network/radv-internal.h3
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c2
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c1
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c239
-rw-r--r--src/libsystemd-network/sd-dhcp6-lease.c36
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c1
-rw-r--r--src/libsystemd-network/sd-radv.c98
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c234
-rw-r--r--src/libsystemd-network/test-lldp.c1
-rw-r--r--src/libsystemd-network/test-ndisc-ra.c4
-rw-r--r--src/libsystemd/libsystemd.sym19
-rw-r--r--src/libsystemd/meson.build12
-rw-r--r--src/libsystemd/sd-bus/bus-bloom.c157
-rw-r--r--src/libsystemd/sd-bus/bus-container.c36
-rw-r--r--src/libsystemd/sd-bus/bus-control.c457
-rw-r--r--src/libsystemd/sd-bus/bus-control.h4
-rw-r--r--src/libsystemd/sd-bus/bus-convenience.c70
-rw-r--r--src/libsystemd/sd-bus/bus-gvariant.c5
-rw-r--r--src/libsystemd/sd-bus/bus-internal.c15
-rw-r--r--src/libsystemd/sd-bus/bus-internal.h103
-rw-r--r--src/libsystemd/sd-bus/bus-kernel.c46
-rw-r--r--src/libsystemd/sd-bus/bus-kernel.h2
-rw-r--r--src/libsystemd/sd-bus/bus-message.c18
-rw-r--r--src/libsystemd/sd-bus/bus-message.h4
-rw-r--r--src/libsystemd/sd-bus/bus-objects.c14
-rw-r--r--src/libsystemd/sd-bus/bus-signature.c2
-rw-r--r--src/libsystemd/sd-bus/bus-slot.c13
-rw-r--r--src/libsystemd/sd-bus/bus-socket.c335
-rw-r--r--src/libsystemd/sd-bus/bus-socket.h1
-rw-r--r--src/libsystemd/sd-bus/bus-track.c31
-rw-r--r--src/libsystemd/sd-bus/bus-type.c4
-rw-r--r--src/libsystemd/sd-bus/bus-type.h2
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c968
-rw-r--r--src/libsystemd/sd-bus/test-bus-benchmark.c2
-rw-r--r--src/libsystemd/sd-bus/test-bus-chat.c4
-rw-r--r--src/libsystemd/sd-bus/test-bus-track.c3
-rw-r--r--src/libsystemd/sd-bus/test-bus-watch-bind.c239
-rw-r--r--src/libsystemd/sd-daemon/sd-daemon.c44
-rw-r--r--src/libsystemd/sd-device/sd-device.c37
-rw-r--r--src/libsystemd/sd-event/sd-event.c49
-rw-r--r--src/libsystemd/sd-event/test-event.c3
-rw-r--r--src/libsystemd/sd-id128/id128-util.c1
-rw-r--r--src/libsystemd/sd-login/sd-login.c7
-rw-r--r--src/libsystemd/sd-netlink/generic-netlink.c97
-rw-r--r--src/libsystemd/sd-netlink/local-addresses.c2
-rw-r--r--src/libsystemd/sd-netlink/netlink-internal.h6
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c71
-rw-r--r--src/libsystemd/sd-netlink/netlink-socket.c27
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c118
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.h6
-rw-r--r--src/libsystemd/sd-netlink/netlink-util.c4
-rw-r--r--src/libsystemd/sd-netlink/netlink-util.h2
-rw-r--r--src/libsystemd/sd-netlink/sd-netlink.c21
-rw-r--r--src/libsystemd/sd-netlink/test-local-addresses.c4
-rw-r--r--src/libsystemd/sd-netlink/test-netlink.c24
-rw-r--r--src/libsystemd/sd-resolve/sd-resolve.c35
-rw-r--r--src/libsystemd/sd-resolve/test-resolve.c4
-rw-r--r--src/libudev/libudev-queue.c10
-rw-r--r--src/libudev/meson.build17
-rw-r--r--src/locale/localectl.c5
-rw-r--r--src/locale/localed.c4
-rw-r--r--src/login/73-seat-late.rules.m4 (renamed from src/login/73-seat-late.rules.in)5
-rw-r--r--src/login/inhibit.c17
-rw-r--r--src/login/loginctl.c5
-rw-r--r--src/login/logind-core.c1
-rw-r--r--src/login/logind-inhibit.c4
-rw-r--r--src/login/logind-session.c4
-rw-r--r--src/login/logind-user-dbus.c12
-rw-r--r--src/login/logind-user.c21
-rw-r--r--src/login/logind.c98
-rw-r--r--src/login/meson.build30
-rw-r--r--src/login/pam_systemd.c2
-rw-r--r--src/machine/image-dbus.c16
-rw-r--r--src/machine/machine-dbus.c117
-rw-r--r--src/machine/machinectl.c42
-rw-r--r--src/machine/machined-dbus.c9
-rw-r--r--src/machine/machined.c99
-rw-r--r--src/mount/mount-tool.c69
-rw-r--r--src/network/meson.build6
-rw-r--r--src/network/netdev/ipvlan.c17
-rw-r--r--src/network/netdev/ipvlan.h17
-rw-r--r--src/network/netdev/netdev-gperf.gperf234
-rw-r--r--src/network/netdev/netdev.c51
-rw-r--r--src/network/netdev/netdev.h11
-rw-r--r--src/network/netdev/tunnel.c7
-rw-r--r--src/network/netdev/tunnel.h1
-rw-r--r--src/network/netdev/tuntap.c1
-rw-r--r--src/network/netdev/veth.c3
-rw-r--r--src/network/netdev/vlan.c1
-rw-r--r--src/network/netdev/wireguard.c721
-rw-r--r--src/network/netdev/wireguard.h98
-rw-r--r--src/network/networkd-address.c248
-rw-r--r--src/network/networkd-address.h22
-rw-r--r--src/network/networkd-dhcp4.c21
-rw-r--r--src/network/networkd-dhcp6.c249
-rw-r--r--src/network/networkd-link.c18
-rw-r--r--src/network/networkd-manager.c447
-rw-r--r--src/network/networkd-manager.h11
-rw-r--r--src/network/networkd-network-gperf.gperf9
-rw-r--r--src/network/networkd-network.c8
-rw-r--r--src/network/networkd-network.h14
-rw-r--r--src/network/networkd-radv.c301
-rw-r--r--src/network/networkd-radv.h26
-rw-r--r--src/network/networkd-route.c101
-rw-r--r--src/network/networkd-route.h6
-rw-r--r--src/network/networkd-routing-policy-rule.c17
-rw-r--r--src/network/networkd-routing-policy-rule.h2
-rw-r--r--src/network/networkd.c32
-rw-r--r--src/network/test-routing-policy-rule.c9
-rw-r--r--src/notify/notify.c40
-rw-r--r--src/nspawn/nspawn-mount.c9
-rw-r--r--src/nspawn/nspawn-register.c4
-rw-r--r--src/nspawn/nspawn-settings.h2
-rw-r--r--src/nspawn/nspawn-setuid.c17
-rw-r--r--src/nspawn/nspawn-stub-pid1.c3
-rw-r--r--src/nspawn/nspawn.c55
-rw-r--r--src/nss-resolve/nss-resolve.c9
-rw-r--r--src/nss-systemd/nss-systemd.c12
-rw-r--r--src/partition/growfs.c1
-rw-r--r--src/partition/makefs.c17
-rw-r--r--src/quotacheck/quotacheck.c29
-rw-r--r--src/rc-local-generator/rc-local-generator.c25
-rw-r--r--src/remount-fs/remount-fs.c13
-rw-r--r--src/resolve/dns-type.c1
-rw-r--r--src/resolve/meson.build33
-rw-r--r--src/resolve/resolve-tool.c10
-rw-r--r--src/resolve/resolved-bus.c55
-rw-r--r--src/resolve/resolved-def.h4
-rw-r--r--src/resolve/resolved-dns-cache.c2
-rw-r--r--src/resolve/resolved-dns-packet.c240
-rw-r--r--src/resolve/resolved-dns-query.c7
-rw-r--r--src/resolve/resolved-dns-rr.c6
-rw-r--r--src/resolve/resolved-dns-scope.c22
-rw-r--r--src/resolve/resolved-dns-server.c2
-rw-r--r--src/resolve/resolved-manager.c5
-rw-r--r--src/resolve/test-dns-packet.c2
-rw-r--r--src/resolve/test-dnssec-complex.c7
-rw-r--r--src/resolve/test-dnssec.c2
-rw-r--r--src/run/run.c120
-rw-r--r--src/shared/ask-password-api.c4
-rw-r--r--src/shared/bootspec.c81
-rw-r--r--src/shared/bus-unit-util.c1904
-rw-r--r--src/shared/bus-unit-util.h10
-rw-r--r--src/shared/bus-util.c112
-rw-r--r--src/shared/bus-util.h3
-rw-r--r--src/shared/condition.c99
-rw-r--r--src/shared/condition.h17
-rw-r--r--src/shared/dissect-image.c33
-rw-r--r--src/shared/dissect-image.h2
-rw-r--r--src/shared/efivars.h3
-rw-r--r--src/shared/generator.c34
-rw-r--r--src/shared/generator.h6
-rw-r--r--src/shared/install.c2
-rw-r--r--src/shared/logs-show.c123
-rw-r--r--src/shared/logs-show.h1
-rw-r--r--src/shared/loop-util.c1
-rw-r--r--src/shared/machine-pool.c32
-rw-r--r--src/shared/meson.build28
-rw-r--r--src/shared/nsflags.h7
-rw-r--r--src/shared/pager.c68
-rw-r--r--src/shared/seccomp-util.c64
-rw-r--r--src/shared/seccomp-util.h18
-rw-r--r--src/shared/spawn-ask-password-agent.c9
-rw-r--r--src/shared/spawn-polkit-agent.c16
-rw-r--r--src/shared/sysctl-util.c17
-rw-r--r--src/shared/test-tables.h1
-rw-r--r--src/shared/tests.c4
-rw-r--r--src/shared/utmp-wtmp.c2
-rw-r--r--src/shared/volatile-util.c2
-rw-r--r--src/shared/wireguard-netlink.h179
-rw-r--r--src/sulogin-shell/sulogin-shell.c17
-rw-r--r--src/system-update-generator/system-update-generator.c3
-rw-r--r--src/systemctl/systemctl.c325
-rw-r--r--src/systemd/sd-bus.h20
-rw-r--r--src/systemd/sd-dhcp6-client.h5
-rw-r--r--src/systemd/sd-dhcp6-lease.h5
-rw-r--r--src/systemd/sd-event.h5
-rw-r--r--src/systemd/sd-netlink.h9
-rw-r--r--src/systemd/sd-radv.h5
-rw-r--r--src/sysusers/sysusers.c164
-rw-r--r--src/sysv-generator/sysv-generator.c3
-rw-r--r--src/test/meson.build31
-rw-r--r--src/test/test-async.c2
-rw-r--r--src/test/test-capability.c2
-rw-r--r--src/test/test-cgroup-util.c17
-rw-r--r--src/test/test-cgroup.c1
-rw-r--r--src/test/test-condition.c201
-rw-r--r--src/test/test-extract-word.c1
-rw-r--r--src/test/test-fs-util.c144
-rw-r--r--src/test/test-hash.c1
-rw-r--r--src/test/test-hexdecoct.c2
-rw-r--r--src/test/test-log.c1
-rw-r--r--src/test/test-ns.c1
-rw-r--r--src/test/test-parse-util.c1
-rw-r--r--src/test/test-process-util.c48
-rw-r--r--src/test/test-procfs-util.c38
-rw-r--r--src/test/test-seccomp.c20
-rw-r--r--src/test/test-signal-util.c1
-rw-r--r--src/test/test-sizeof.c2
-rw-r--r--src/test/test-socket-util.c69
-rw-r--r--src/test/test-strip-tab-ansi.c8
-rw-r--r--src/test/test-tmpfiles.c1
-rw-r--r--src/test/test-unit-name.c9
-rw-r--r--src/test/test-util.c1
-rw-r--r--src/test/test-watch-pid.c96
-rw-r--r--src/test/test-watchdog.c1
-rw-r--r--src/timedate/timedatectl.c7
-rw-r--r--src/timedate/timedated.c4
-rw-r--r--src/timesync/timesyncd-gperf.gperf2
-rw-r--r--src/timesync/timesyncd-manager.c2
-rw-r--r--src/timesync/timesyncd.c18
-rw-r--r--src/tmpfiles/tmpfiles.c194
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c24
-rwxr-xr-xsrc/udev/generate-keyboard-keys-gperf.sh3
-rwxr-xr-xsrc/udev/generate-keyboard-keys-list.sh3
-rw-r--r--src/udev/meson.build32
-rw-r--r--src/udev/mtd_probe/probe_smartmedia.c12
-rw-r--r--src/udev/net/ethtool-util.c55
-rw-r--r--src/udev/net/link-config-gperf.gperf3
-rw-r--r--src/udev/net/link-config.c10
-rw-r--r--src/udev/net/link-config.h3
-rw-r--r--src/udev/scsi_id/scsi_id.c6
-rw-r--r--src/udev/udev-builtin-input_id.c3
-rw-r--r--src/udev/udev-builtin-net_id.c2
-rw-r--r--src/udev/udev-event.c33
-rw-r--r--src/udev/udevadm-control.c1
-rw-r--r--src/udev/udevd.c6
-rw-r--r--src/vconsole/vconsole-setup.c34
-rw-r--r--src/veritysetup/veritysetup-generator.c24
-rw-r--r--src/veritysetup/veritysetup.c5
427 files changed, 15587 insertions, 7702 deletions
diff --git a/src/activate/activate.c b/src/activate/activate.c
index 83807efdcf..c07dcb8626 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -267,38 +267,23 @@ static int exec_process(const char* name, char **argv, char **env, int start_fd,
static int fork_and_exec_process(const char* child, char** argv, char **env, int fd) {
_cleanup_free_ char *joined = NULL;
- pid_t parent_pid, child_pid;
+ pid_t child_pid;
+ int r;
joined = strv_join(argv, " ");
if (!joined)
return log_oom();
- parent_pid = getpid_cached();
-
- child_pid = fork();
- if (child_pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
-
- /* In the child */
- if (child_pid == 0) {
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- /* Make sure the child goes away when the parent dies */
- if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
- _exit(EXIT_FAILURE);
-
- /* Check whether our parent died before we were able
- * to set the death signal */
- if (getppid() != parent_pid)
- _exit(EXIT_SUCCESS);
-
+ r = safe_fork("(activate)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* In the child */
exec_process(child, argv, env, fd, 1);
_exit(EXIT_FAILURE);
}
- log_info("Spawned %s (%s) as PID %d", child, joined, child_pid);
+ log_info("Spawned %s (%s) as PID " PID_FMT ".", child, joined, child_pid);
return 0;
}
diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c
index 461e7852ac..f475b6598c 100644
--- a/src/analyze/analyze-verify.c
+++ b/src/analyze/analyze-verify.c
@@ -220,7 +220,7 @@ static int verify_unit(Unit *u, bool check_man) {
assert(u);
- if (log_get_max_level() >= LOG_DEBUG)
+ if (DEBUG_LOGGING)
unit_dump(u, stdout, "\t");
log_unit_debug(u, "Creating %s/start job", u->id);
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index d45c1dc496..834620a4cd 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -47,6 +47,7 @@
#include "terminal-util.h"
#include "unit-name.h"
#include "util.h"
+#include "verbs.h"
#define SCALE_X (0.1 / 1000.0) /* pixels per us */
#define SCALE_Y (20.0)
@@ -78,7 +79,7 @@ static char** arg_dot_to_patterns = NULL;
static usec_t arg_fuzz = 0;
static bool arg_no_pager = false;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
-static char *arg_host = NULL;
+static const char *arg_host = NULL;
static bool arg_user = false;
static bool arg_man = true;
static bool arg_generators = false;
@@ -130,6 +131,13 @@ struct host_info {
char *architecture;
};
+static int acquire_bus(bool need_full_bus, sd_bus **bus) {
+ if (need_full_bus)
+ return bus_connect_transport(arg_transport, arg_host, arg_user, bus);
+ else
+ return bus_connect_transport_systemd(arg_transport, arg_host, arg_user, bus);
+}
+
static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
@@ -521,7 +529,7 @@ static int pretty_boot_time(sd_bus *bus, char **_buf) {
"ActiveEnterTimestampMonotonic",
&activated_time);
if (r < 0) {
- log_info_errno(r, "default.target seems not to be started. Continuing...");
+ log_info_errno(r, "Could not get time to reach default.target. Continuing...");
activated_time = USEC_INFINITY;
}
@@ -541,8 +549,15 @@ static int pretty_boot_time(sd_bus *bus, char **_buf) {
size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
- if (unit_id && activated_time != USEC_INFINITY)
+ if (unit_id && (activated_time > 0 && activated_time != USEC_INFINITY))
size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id, format_timespan(ts, sizeof(ts), activated_time - t->userspace_time, USEC_PER_MSEC));
+ else if (unit_id && activated_time == 0)
+ size = strpcpyf(&ptr, size, "\n%s was never reached", unit_id);
+ else if (unit_id && activated_time == USEC_INFINITY)
+ size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.",unit_id);
+ else if (!unit_id)
+ size = strpcpyf(&ptr, size, "\ncould not find default.target");
+
ptr = strdup(buf);
if (!ptr)
@@ -575,15 +590,20 @@ static void svg_graph_box(double height, double begin, double end) {
}
}
-static int analyze_plot(sd_bus *bus) {
+static int analyze_plot(int argc, char *argv[], void *userdata) {
_cleanup_(free_host_infop) struct host_info *host = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
struct unit_times *times;
struct boot_times *boot;
- int n, m = 1, y=0;
+ int n, m = 1, y = 0, r;
double width;
_cleanup_free_ char *pretty_times = NULL;
struct unit_times *u;
+ r = acquire_bus(true, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
+
n = acquire_boot_times(bus, &boot);
if (n < 0)
return n;
@@ -977,24 +997,29 @@ static int list_dependencies(sd_bus *bus, const char *name) {
return list_dependencies_one(bus, name, 0, &units, 0);
}
-static int analyze_critical_chain(sd_bus *bus, char *names[]) {
+static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
struct unit_times *times;
unsigned int i;
Hashmap *h;
int n, r;
+ r = acquire_bus(false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
+
n = acquire_time_data(bus, &times);
if (n <= 0)
return n;
h = hashmap_new(&string_hash_ops);
if (!h)
- return -ENOMEM;
+ return log_oom();
- for (i = 0; i < (unsigned)n; i++) {
+ for (i = 0; i < (unsigned) n; i++) {
r = hashmap_put(h, times[i].name, &times[i]);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add entry to hashmap: %m");
}
unit_times_hashmap = h;
@@ -1003,22 +1028,27 @@ static int analyze_critical_chain(sd_bus *bus, char *names[]) {
puts("The time after the unit is active or started is printed after the \"@\" character.\n"
"The time the unit takes to start is printed after the \"+\" character.\n");
- if (!strv_isempty(names)) {
+ if (argc > 1) {
char **name;
- STRV_FOREACH(name, names)
+ STRV_FOREACH(name, strv_skip(argv, 1))
list_dependencies(bus, *name);
} else
list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
- hashmap_free(h);
+ h = hashmap_free(h);
free_unit_times(times, (unsigned) n);
return 0;
}
-static int analyze_blame(sd_bus *bus) {
+static int analyze_blame(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
struct unit_times *times;
unsigned i;
- int n;
+ int n, r;
+
+ r = acquire_bus(false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
n = acquire_time_data(bus, &times);
if (n <= 0)
@@ -1039,10 +1069,15 @@ static int analyze_blame(sd_bus *bus) {
return 0;
}
-static int analyze_time(sd_bus *bus) {
+static int analyze_time(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *buf = NULL;
int r;
+ r = acquire_bus(false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
+
r = pretty_boot_time(bus, &buf);
if (r < 0)
return r;
@@ -1163,16 +1198,21 @@ static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
return 0;
}
-static int dot(sd_bus *bus, char* patterns[]) {
+static int dot(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_strv_free_ char **expanded_patterns = NULL;
_cleanup_strv_free_ char **expanded_from_patterns = NULL;
_cleanup_strv_free_ char **expanded_to_patterns = NULL;
int r;
UnitInfo u;
- r = expand_patterns(bus, patterns, &expanded_patterns);
+ r = acquire_bus(false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
+
+ r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns);
if (r < 0)
return r;
@@ -1228,28 +1268,28 @@ static int dot(sd_bus *bus, char* patterns[]) {
return 0;
}
-static int dump(sd_bus *bus, char **args) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+static int dump(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
const char *text = NULL;
int r;
- if (!strv_isempty(args)) {
- log_error("Too many arguments.");
- return -E2BIG;
- }
+ r = acquire_bus(false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
pager_open(arg_no_pager, false);
r = sd_bus_call_method(
bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "Dump",
- &error,
- &reply,
- "");
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "Dump",
+ &error,
+ &reply,
+ NULL);
if (r < 0)
return log_error_errno(r, "Failed issue method call: %s", bus_error_message(&error, r));
@@ -1261,17 +1301,17 @@ static int dump(sd_bus *bus, char **args) {
return 0;
}
-static int set_log_level(sd_bus *bus, char **args) {
+static int set_log_level(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
- assert(bus);
- assert(args);
+ assert(argc == 2);
+ assert(argv);
- if (strv_length(args) != 1) {
- log_error("This command expects one argument only.");
- return -E2BIG;
- }
+ r = acquire_bus(false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
r = sd_bus_set_property(
bus,
@@ -1281,25 +1321,22 @@ static int set_log_level(sd_bus *bus, char **args) {
"LogLevel",
&error,
"s",
- args[0]);
+ argv[1]);
if (r < 0)
return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
return 0;
}
-static int get_log_level(sd_bus *bus, char **args) {
+static int get_log_level(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *level = NULL;
+ int r;
- assert(bus);
- assert(args);
-
- if (!strv_isempty(args)) {
- log_error("Too many arguments.");
- return -E2BIG;
- }
+ r = acquire_bus(false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
r = sd_bus_get_property_string(
bus,
@@ -1316,17 +1353,21 @@ static int get_log_level(sd_bus *bus, char **args) {
return 0;
}
-static int set_log_target(sd_bus *bus, char **args) {
+static int get_or_set_log_level(int argc, char *argv[], void *userdata) {
+ return (argc == 1) ? get_log_level(argc, argv, userdata) : set_log_level(argc, argv, userdata);
+}
+
+static int set_log_target(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
- assert(bus);
- assert(args);
+ assert(argc == 2);
+ assert(argv);
- if (strv_length(args) != 1) {
- log_error("This command expects one argument only.");
- return -E2BIG;
- }
+ r = acquire_bus(false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
r = sd_bus_set_property(
bus,
@@ -1336,25 +1377,22 @@ static int set_log_target(sd_bus *bus, char **args) {
"LogTarget",
&error,
"s",
- args[0]);
+ argv[1]);
if (r < 0)
return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
return 0;
}
-static int get_log_target(sd_bus *bus, char **args) {
+static int get_log_target(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *target = NULL;
+ int r;
- assert(bus);
- assert(args);
-
- if (!strv_isempty(args)) {
- log_error("Too many arguments.");
- return -E2BIG;
- }
+ r = acquire_bus(false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
r = sd_bus_get_property_string(
bus,
@@ -1371,6 +1409,10 @@ static int get_log_target(sd_bus *bus, char **args) {
return 0;
}
+static int get_or_set_log_target(int argc, char *argv[], void *userdata) {
+ return (argc == 1) ? get_log_target(argc, argv, userdata) : set_log_target(argc, argv, userdata);
+}
+
#if HAVE_SECCOMP
static void dump_syscall_filter(const SyscallFilterSet *set) {
const char *syscall;
@@ -1381,12 +1423,12 @@ static void dump_syscall_filter(const SyscallFilterSet *set) {
printf(" %s\n", syscall);
}
-static int dump_syscall_filters(char** names) {
+static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
bool first = true;
pager_open(arg_no_pager, false);
- if (strv_isempty(names)) {
+ if (strv_isempty(strv_skip(argv, 1))) {
int i;
for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
@@ -1398,7 +1440,7 @@ static int dump_syscall_filters(char** names) {
} else {
char **name;
- STRV_FOREACH(name, names) {
+ STRV_FOREACH(name, strv_skip(argv, 1)) {
const SyscallFilterSet *set;
if (!first)
@@ -1422,25 +1464,20 @@ static int dump_syscall_filters(char** names) {
}
#else
-static int dump_syscall_filters(char** names) {
+static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
log_error("Not compiled with syscall filters, sorry.");
return -EOPNOTSUPP;
}
#endif
-static int test_calendar(char **args) {
+static int test_calendar(int argc, char *argv[], void *userdata) {
int ret = 0, r;
char **p;
usec_t n;
- if (strv_isempty(args)) {
- log_error("Expected at least one calendar specification string as argument.");
- return -EINVAL;
- }
-
n = now(CLOCK_REALTIME);
- STRV_FOREACH(p, args) {
+ STRV_FOREACH(p, strv_skip(argv, 1)) {
_cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
_cleanup_free_ char *t = NULL;
usec_t next;
@@ -1492,7 +1529,67 @@ static int test_calendar(char **args) {
return ret;
}
-static void help(void) {
+static int service_watchdogs(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int b, r;
+
+ assert(IN_SET(argc, 1, 2));
+ assert(argv);
+
+ r = acquire_bus(false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
+
+ /* get ServiceWatchdogs */
+ if (argc == 1) {
+ r = sd_bus_get_property_trivial(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ServiceWatchdogs",
+ &error,
+ 'b',
+ &b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r));
+
+ printf("%s\n", yes_no(!!b));
+
+ return 0;
+ }
+
+ /* set ServiceWatchdogs */
+ b = parse_boolean(argv[1]);
+ if (b < 0) {
+ log_error("Failed to parse service-watchdogs argument.");
+ return -EINVAL;
+ }
+
+ r = sd_bus_set_property(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ServiceWatchdogs",
+ &error,
+ "b",
+ b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r));
+
+ return 0;
+}
+
+static int do_verify(int argc, char *argv[], void *userdata) {
+ return verify_units(strv_skip(argv, 1),
+ arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM,
+ arg_man,
+ arg_generators);
+}
+
+static int help(int argc, char *argv[], void *userdata) {
pager_open(arg_no_pager, false);
@@ -1516,22 +1613,23 @@ static void help(void) {
"Commands:\n"
" time Print time spent in the kernel\n"
" blame Print list of running units ordered by time to init\n"
- " critical-chain Print a tree of the time critical chain of units\n"
+ " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
" plot Output SVG graphic showing service initialization\n"
- " dot Output dependency graph in man:dot(1) format\n"
- " set-log-level LEVEL Set logging threshold for manager\n"
- " set-log-target TARGET Set logging target for manager\n"
- " get-log-level Get logging threshold for manager\n"
- " get-log-target Get logging target for manager\n"
+ " dot [UNIT...] Output dependency graph in man:dot(1) format\n"
+ " log-level [LEVEL] Get/set logging threshold for manager\n"
+ " log-target [TARGET] Get/set logging target for manager\n"
" dump Output state serialization of service manager\n"
" syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
" verify FILE... Check unit files for correctness\n"
" calendar SPEC... Validate repetitive calendar time events\n"
+ " service-watchdogs [BOOL] Get/set service watchdog state\n"
, program_invocation_short_name);
/* When updating this list, including descriptions, apply
* changes to shell-completion/bash/systemd-analyze and
* shell-completion/zsh/_systemd-analyze too. */
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -1576,8 +1674,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help(0, NULL, NULL);
case ARG_VERSION:
return version();
@@ -1669,10 +1766,34 @@ static int parse_argv(int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
+
+ static const Verb verbs[] = {
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time },
+ { "blame", VERB_ANY, 1, 0, analyze_blame },
+ { "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain },
+ { "plot", VERB_ANY, 1, 0, analyze_plot },
+ { "dot", VERB_ANY, VERB_ANY, 0, dot },
+ { "log-level", VERB_ANY, 2, 0, get_or_set_log_level },
+ { "log-target", VERB_ANY, 2, 0, get_or_set_log_target },
+ /* The following four verbs are deprecated aliases */
+ { "set-log-level", 2, 2, 0, set_log_level },
+ { "get-log-level", VERB_ANY, 1, 0, get_log_level },
+ { "set-log-target", 2, 2, 0, set_log_target },
+ { "get-log-target", VERB_ANY, 1, 0, get_log_target },
+ { "dump", VERB_ANY, 1, 0, dump },
+ { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
+ { "verify", 2, VERB_ANY, 0, do_verify },
+ { "calendar", 2, VERB_ANY, 0, test_calendar },
+ { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
+ {}
+ };
+
int r;
setlocale(LC_ALL, "");
setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
+
log_parse_environment();
log_open();
@@ -1680,47 +1801,7 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
- if (streq_ptr(argv[optind], "verify"))
- r = verify_units(argv+optind+1,
- arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM,
- arg_man,
- arg_generators);
- else {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
-
- r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
- if (r < 0) {
- log_error_errno(r, "Failed to create bus connection: %m");
- goto finish;
- }
-
- if (!argv[optind] || streq(argv[optind], "time"))
- r = analyze_time(bus);
- else if (streq(argv[optind], "blame"))
- r = analyze_blame(bus);
- else if (streq(argv[optind], "critical-chain"))
- r = analyze_critical_chain(bus, argv+optind+1);
- else if (streq(argv[optind], "plot"))
- r = analyze_plot(bus);
- else if (streq(argv[optind], "dot"))
- r = dot(bus, argv+optind+1);
- else if (streq(argv[optind], "dump"))
- r = dump(bus, argv+optind+1);
- else if (streq(argv[optind], "set-log-level"))
- r = set_log_level(bus, argv+optind+1);
- else if (streq(argv[optind], "get-log-level"))
- r = get_log_level(bus, argv+optind+1);
- else if (streq(argv[optind], "set-log-target"))
- r = set_log_target(bus, argv+optind+1);
- else if (streq(argv[optind], "get-log-target"))
- r = get_log_target(bus, argv+optind+1);
- else if (streq(argv[optind], "syscall-filter"))
- r = dump_syscall_filters(argv+optind+1);
- else if (streq(argv[optind], "calendar"))
- r = test_calendar(argv+optind+1);
- else
- log_error("Unknown operation '%s'.", argv[optind]);
- }
+ r = dispatch_verb(argc, argv, verbs, NULL);
finish:
pager_close();
diff --git a/src/basic/af-list.h b/src/basic/af-list.h
index 65656022cc..1684bc6a0f 100644
--- a/src/basic/af-list.h
+++ b/src/basic/af-list.h
@@ -20,6 +20,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <sys/socket.h>
+
#include "string-util.h"
const char *af_to_name(int id);
diff --git a/src/basic/async.c b/src/basic/async.c
index d368b92522..b6c6d6a80b 100644
--- a/src/basic/async.c
+++ b/src/basic/async.c
@@ -27,49 +27,82 @@
#include "fd-util.h"
#include "log.h"
#include "macro.h"
+#include "process-util.h"
+#include "signal-util.h"
#include "util.h"
int asynchronous_job(void* (*func)(void *p), void *arg) {
+ sigset_t ss, saved_ss;
pthread_attr_t a;
pthread_t t;
- int r;
+ int r, k;
- /* It kinda sucks that we have to resort to threads to
- * implement an asynchronous sync(), but well, such is
- * life.
- *
- * Note that issuing this command right before exiting a
- * process will cause the process to wait for the sync() to
- * complete. This function hence is nicely asynchronous really
- * only in long running processes. */
+ /* It kinda sucks that we have to resort to threads to implement an asynchronous close(), but well, such is
+ * life. */
r = pthread_attr_init(&a);
if (r > 0)
return -r;
r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
- if (r > 0)
+ if (r > 0) {
+ r = -r;
+ goto finish;
+ }
+
+ if (sigfillset(&ss) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ /* Block all signals before forking off the thread, so that the new thread is started with all signals
+ * blocked. This way the existence of the new thread won't affect signal handling in other threads. */
+
+ r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss);
+ if (r > 0) {
+ r = -r;
goto finish;
+ }
r = pthread_create(&t, &a, func, arg);
+ k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL);
+
+ if (r > 0)
+ r = -r;
+ else if (k > 0)
+ r = -k;
+ else
+ r = 0;
+
finish:
pthread_attr_destroy(&a);
- return -r;
+ return r;
}
-static void *sync_thread(void *p) {
- sync();
- return NULL;
-}
+int asynchronous_sync(pid_t *ret_pid) {
+ int r;
-int asynchronous_sync(void) {
- log_debug("Spawning new thread for sync");
+ /* This forks off an invocation of fork() as a child process, in order to initiate synchronization to
+ * disk. Note that we implement this as helper process rather than thread as we don't want the sync() to hang our
+ * original process ever, and a thread would do that as the process can't exit with threads hanging in blocking
+ * syscalls. */
+
+ r = safe_fork("(sd-sync)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, ret_pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child process */
+ (void) sync();
+ _exit(EXIT_SUCCESS);
+ }
- return asynchronous_job(sync_thread, NULL);
+ return 0;
}
static void *close_thread(void *p) {
+ (void) pthread_setname_np(pthread_self(), "close");
+
assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF);
return NULL;
}
diff --git a/src/basic/async.h b/src/basic/async.h
index 7eac54d8b2..01c975bb30 100644
--- a/src/basic/async.h
+++ b/src/basic/async.h
@@ -22,5 +22,5 @@
int asynchronous_job(void* (*func)(void *p), void *arg);
-int asynchronous_sync(void);
+int asynchronous_sync(pid_t *ret_pid);
int asynchronous_close(int fd);
diff --git a/src/basic/blockdev-util.c b/src/basic/blockdev-util.c
new file mode 100644
index 0000000000..3a8f8d1c27
--- /dev/null
+++ b/src/basic/blockdev-util.c
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/stat.h>
+#include <sys/statfs.h>
+
+#include "alloc-util.h"
+#include "blockdev-util.h"
+#include "btrfs-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "missing.h"
+#include "stat-util.h"
+
+int block_get_whole_disk(dev_t d, dev_t *ret) {
+ char p[SYS_BLOCK_PATH_MAX("/partition")];
+ _cleanup_free_ char *s = NULL;
+ unsigned n, m;
+ int r;
+
+ assert(ret);
+
+ /* If it has a queue this is good enough for us */
+ xsprintf_sys_block_path(p, "/queue", d);
+ if (access(p, F_OK) >= 0) {
+ *ret = d;
+ return 0;
+ }
+
+ /* If it is a partition find the originating device */
+ xsprintf_sys_block_path(p, "/partition", d);
+ if (access(p, F_OK) < 0)
+ return -ENOENT;
+
+ /* Get parent dev_t */
+ xsprintf_sys_block_path(p, "/../dev", d);
+ r = read_one_line_file(p, &s);
+ if (r < 0)
+ return r;
+
+ r = sscanf(s, "%u:%u", &m, &n);
+ if (r != 2)
+ return -EINVAL;
+
+ /* Only return this if it is really good enough for us. */
+ xsprintf_sys_block_path(p, "/queue", makedev(m, n));
+ if (access(p, F_OK) < 0)
+ return -ENOENT;
+
+ *ret = makedev(m, n);
+ return 0;
+}
+
+int get_block_device(const char *path, dev_t *dev) {
+ struct stat st;
+ struct statfs sfs;
+
+ assert(path);
+ assert(dev);
+
+ /* Get's the block device directly backing a file system. If
+ * the block device is encrypted, returns the device mapper
+ * block device. */
+
+ if (lstat(path, &st))
+ return -errno;
+
+ if (major(st.st_dev) != 0) {
+ *dev = st.st_dev;
+ return 1;
+ }
+
+ if (statfs(path, &sfs) < 0)
+ return -errno;
+
+ if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
+ return btrfs_get_block_device(path, dev);
+
+ return 0;
+}
+
+int get_block_device_harder(const char *path, dev_t *dev) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_free_ char *t = NULL;
+ char p[SYS_BLOCK_PATH_MAX("/slaves")];
+ struct dirent *de, *found = NULL;
+ const char *q;
+ unsigned maj, min;
+ dev_t dt;
+ int r;
+
+ assert(path);
+ assert(dev);
+
+ /* Gets the backing block device for a file system, and
+ * handles LUKS encrypted file systems, looking for its
+ * immediate parent, if there is one. */
+
+ r = get_block_device(path, &dt);
+ if (r <= 0)
+ return r;
+
+ xsprintf_sys_block_path(p, "/slaves", dt);
+ d = opendir(p);
+ if (!d) {
+ if (errno == ENOENT)
+ goto fallback;
+
+ return -errno;
+ }
+
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
+
+ if (dot_or_dot_dot(de->d_name))
+ continue;
+
+ if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
+ continue;
+
+ if (found) {
+ _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
+
+ /* We found a device backed by multiple other devices. We don't really support automatic
+ * discovery on such setups, with the exception of dm-verity partitions. In this case there are
+ * two backing devices: the data partition and the hash partition. We are fine with such
+ * setups, however, only if both partitions are on the same physical device. Hence, let's
+ * verify this. */
+
+ u = strjoin(p, "/", de->d_name, "/../dev");
+ if (!u)
+ return -ENOMEM;
+
+ v = strjoin(p, "/", found->d_name, "/../dev");
+ if (!v)
+ return -ENOMEM;
+
+ r = read_one_line_file(u, &a);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read %s: %m", u);
+ goto fallback;
+ }
+
+ r = read_one_line_file(v, &b);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read %s: %m", v);
+ goto fallback;
+ }
+
+ /* Check if the parent device is the same. If not, then the two backing devices are on
+ * different physical devices, and we don't support that. */
+ if (!streq(a, b))
+ goto fallback;
+ }
+
+ found = de;
+ }
+
+ if (!found)
+ goto fallback;
+
+ q = strjoina(p, "/", found->d_name, "/dev");
+
+ r = read_one_line_file(q, &t);
+ if (r == -ENOENT)
+ goto fallback;
+ if (r < 0)
+ return r;
+
+ if (sscanf(t, "%u:%u", &maj, &min) != 2)
+ return -EINVAL;
+
+ if (maj == 0)
+ goto fallback;
+
+ *dev = makedev(maj, min);
+ return 1;
+
+fallback:
+ *dev = dt;
+ return 1;
+}
diff --git a/src/libsystemd/sd-bus/bus-bloom.h b/src/basic/blockdev-util.h
index 2650762145..642b7ce522 100644
--- a/src/libsystemd/sd-bus/bus-bloom.h
+++ b/src/basic/blockdev-util.h
@@ -4,7 +4,7 @@
/***
This file is part of systemd.
- Copyright 2013 Lennart Poettering
+ 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
@@ -20,25 +20,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
+#include <sys/types.h>
-/*
- * 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.
- *
- */
+#include "macro.h"
+#include "stdio-util.h"
+#include "string-util.h"
-#define DEFAULT_BLOOM_SIZE (512/8) /* m: filter size */
-#define DEFAULT_BLOOM_N_HASH 8 /* k: number of hash functions */
+#define SYS_BLOCK_PATH_MAX(suffix) \
+ (STRLEN("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen_ptr(suffix))
+#define xsprintf_sys_block_path(buf, suffix, devno) \
+ xsprintf(buf, "/sys/dev/block/%u:%u%s", major(devno), minor(devno), strempty(suffix))
-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);
+int block_get_whole_disk(dev_t d, dev_t *ret);
-bool bloom_validate_parameters(size_t size, unsigned n_hash);
+int get_block_device(const char *path, dev_t *dev);
+
+int get_block_device_harder(const char *path, dev_t *dev);
diff --git a/src/basic/btrfs-ctree.h b/src/basic/btrfs-ctree.h
index 66bdf9736e..c5a4244e37 100644
--- a/src/basic/btrfs-ctree.h
+++ b/src/basic/btrfs-ctree.h
@@ -1,6 +1,7 @@
#pragma once
#include "macro.h"
+#include "missing.h"
#include "sparse-endian.h"
/* Stolen from btrfs' ctree.h */
diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c
index ac96e63531..19d385ab7c 100644
--- a/src/basic/btrfs-util.c
+++ b/src/basic/btrfs-util.c
@@ -38,6 +38,7 @@
#endif
#include "alloc-util.h"
+#include "blockdev-util.h"
#include "btrfs-ctree.h"
#include "btrfs-util.h"
#include "chattr-util.h"
@@ -50,7 +51,6 @@
#include "missing.h"
#include "path-util.h"
#include "rm-rf.h"
-#include "selinux-util.h"
#include "smack-util.h"
#include "sparse-endian.h"
#include "stat-util.h"
@@ -174,24 +174,6 @@ int btrfs_subvol_make(const char *path) {
return 0;
}
-int btrfs_subvol_make_label(const char *path) {
- int r;
-
- assert(path);
-
- r = mac_selinux_create_file_prepare(path, S_IFDIR);
- if (r < 0)
- return r;
-
- r = btrfs_subvol_make(path);
- mac_selinux_create_file_clear();
-
- if (r < 0)
- return r;
-
- return mac_smack_fix(path, false, false);
-}
-
int btrfs_subvol_set_read_only_fd(int fd, bool b) {
uint64_t flags, nflags;
struct stat st;
diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h
index 2c78daa37b..952b3c26da 100644
--- a/src/basic/btrfs-util.h
+++ b/src/basic/btrfs-util.h
@@ -84,7 +84,6 @@ int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
int btrfs_subvol_make(const char *path);
-int btrfs_subvol_make_label(const char *path);
int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags);
int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags);
diff --git a/src/basic/build.h b/src/basic/build.h
index ab2a838ce9..0d078efe6f 100644
--- a/src/basic/build.h
+++ b/src/basic/build.h
@@ -140,6 +140,12 @@
#define _IDN_FEATURE_ "-IDN"
#endif
+#if HAVE_PCRE2
+#define _PCRE2_FEATURE_ "+PCRE2"
+#else
+#define _PCRE2_FEATURE_ "-PCRE2"
+#endif
+
#define _CGROUP_HIEARCHY_ "default-hierarchy=" DEFAULT_HIERARCHY_NAME
#define SYSTEMD_FEATURES \
@@ -163,4 +169,5 @@
_KMOD_FEATURE_ " " \
_IDN2_FEATURE_ " " \
_IDN_FEATURE_ " " \
+ _PCRE2_FEATURE_ " " \
_CGROUP_HIEARCHY_
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c
index e6add0c383..fd78022773 100644
--- a/src/basic/calendarspec.c
+++ b/src/basic/calendarspec.c
@@ -35,6 +35,7 @@
#include "fileio.h"
#include "macro.h"
#include "parse-util.h"
+#include "process-util.h"
#include "string-util.h"
#include "time-util.h"
@@ -156,6 +157,11 @@ static void fix_year(CalendarComponent *c) {
int calendar_spec_normalize(CalendarSpec *c) {
assert(c);
+ if (streq_ptr(c->timezone, "UTC")) {
+ c->utc = true;
+ c->timezone = mfree(c->timezone);
+ }
+
if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS)
c->weekdays_bits = -1;
@@ -1348,9 +1354,7 @@ typedef struct SpecNextResult {
} SpecNextResult;
int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) {
- pid_t pid;
- SpecNextResult *shared;
- SpecNextResult tmp;
+ SpecNextResult *shared, tmp;
int r;
if (isempty(spec->timezone))
@@ -1360,15 +1364,12 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
if (shared == MAP_FAILED)
return negative_errno();
- pid = fork();
-
- if (pid == -1) {
- int fork_errno = errno;
+ r = safe_fork("(sd-calendar)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
+ if (r < 0) {
(void) munmap(shared, sizeof *shared);
- return -fork_errno;
+ return r;
}
-
- if (pid == 0) {
+ if (r == 0) {
if (setenv("TZ", spec->timezone, 1) != 0) {
shared->return_value = negative_errno();
_exit(EXIT_FAILURE);
@@ -1381,14 +1382,8 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
_exit(EXIT_SUCCESS);
}
- r = wait_for_terminate(pid, NULL);
- if (r < 0) {
- (void) munmap(shared, sizeof *shared);
- return r;
- }
-
tmp = *shared;
- if (munmap(shared, sizeof *shared) != 0)
+ if (munmap(shared, sizeof *shared) < 0)
return negative_errno();
if (tmp.return_value == 0)
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index f599d0f0f1..7c0ba92110 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -779,13 +779,11 @@ int cg_create(const char *controller, const char *path) {
if (r < 0)
return r;
- if (mkdir(fs, 0755) < 0) {
-
- if (errno == EEXIST)
- return 0;
-
- return -errno;
- }
+ r = mkdir_errno_wrapper(fs, 0755);
+ if (r == -EEXIST)
+ return 0;
+ if (r < 0)
+ return r;
r = cg_hybrid_unified();
if (r < 0)
diff --git a/src/basic/def.h b/src/basic/def.h
index 77ab735aed..43e7e17008 100644
--- a/src/basic/def.h
+++ b/src/basic/def.h
@@ -53,9 +53,10 @@
"/usr/lib/kbd/keymaps/\0"
#endif
-#define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket"
-#define DEFAULT_SYSTEM_BUS_ADDRESS UNIX_SYSTEM_BUS_ADDRESS
-#define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus"
+/* Note that we use the new /run prefix here (instead of /var/run) since we require them to be aliases and that way we
+ * become independent of /var being mounted */
+#define DEFAULT_SYSTEM_BUS_ADDRESS "unix:path=/run/dbus/system_bus_socket"
+#define DEFAULT_USER_BUS_ADDRESS_FMT "unix:path=%s/bus"
#define PLYMOUTH_SOCKET { \
.un.sun_family = AF_UNIX, \
diff --git a/src/basic/device-nodes.h b/src/basic/device-nodes.h
index 7dd8a772a5..49f4ccc729 100644
--- a/src/basic/device-nodes.h
+++ b/src/basic/device-nodes.h
@@ -29,11 +29,6 @@
int encode_devnode_name(const char *str, char *str_enc, size_t len);
int whitelisted_char_for_devnode(char c, const char *additional);
-#define SYS_BLOCK_PATH_MAX(suffix) \
- (STRLEN("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen_ptr(suffix))
-#define xsprintf_sys_block_path(buf, suffix, devno) \
- xsprintf(buf, "/sys/dev/block/%u:%u%s", major(devno), minor(devno), strempty(suffix))
-
#define DEV_NUM_PATH_MAX \
(STRLEN("/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t))
#define xsprintf_dev_num_path(buf, type, devno) \
diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c
index d8eedfc0ad..7eb28b8fd1 100644
--- a/src/basic/errno-list.c
+++ b/src/basic/errno-list.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <string.h>
#include "errno-list.h"
diff --git a/src/basic/errno-list.h b/src/basic/errno-list.h
index 4e9b75a7ea..38beaf96dd 100644
--- a/src/basic/errno-list.h
+++ b/src/basic/errno-list.h
@@ -20,6 +20,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
/*
* MAX_ERRNO is defined as 4095 in linux/err.h
* We use the same value here.
@@ -28,3 +29,6 @@
const char *errno_to_name(int id);
int errno_from_name(const char *name);
+static inline bool errno_is_valid(int n) {
+ return n > 0 && n <= ERRNO_MAX;
+}
diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c
index bbe8bf0006..bfbd1a4931 100644
--- a/src/basic/ether-addr-util.c
+++ b/src/basic/ether-addr-util.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <net/ethernet.h>
#include <stdio.h>
#include <sys/types.h>
diff --git a/src/basic/exec-util.c b/src/basic/exec-util.c
index 82ccbdf5ec..0829b3d836 100644
--- a/src/basic/exec-util.c
+++ b/src/basic/exec-util.c
@@ -48,27 +48,26 @@ assert_cc(EAGAIN == EWOULDBLOCK);
static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
pid_t _pid;
+ int r;
if (null_or_empty_path(path)) {
log_debug("%s is empty (a mask).", path);
return 0;
}
- _pid = fork();
- if (_pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
- if (_pid == 0) {
+ r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
char *_argv[2];
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
if (stdout_fd >= 0) {
/* If the fd happens to be in the right place, go along with that */
if (stdout_fd != STDOUT_FILENO &&
dup2(stdout_fd, STDOUT_FILENO) < 0)
return -errno;
- fd_cloexec(STDOUT_FILENO, false);
+ (void) fd_cloexec(STDOUT_FILENO, false);
}
if (!argv) {
@@ -83,7 +82,6 @@ static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
_exit(EXIT_FAILURE);
}
- log_debug("Spawned %s as " PID_FMT ".", path, _pid);
*pid = _pid;
return 1;
}
@@ -107,11 +105,6 @@ static int do_execute(
* If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
*/
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories);
if (r < 0)
return r;
@@ -153,7 +146,7 @@ static int do_execute(
return log_oom();
t = NULL;
} else {
- r = wait_for_terminate_and_warn(t, pid, true);
+ r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
if (r < 0)
continue;
@@ -183,7 +176,7 @@ static int do_execute(
t = hashmap_remove(pids, PID_TO_PTR(pid));
assert(t);
- wait_for_terminate_and_warn(t, pid, true);
+ (void) wait_for_terminate_and_check(t, pid, WAIT_LOG);
}
return 0;
@@ -196,10 +189,9 @@ int execute_directories(
void* const callback_args[_STDOUT_CONSUME_MAX],
char *argv[]) {
- pid_t executor_pid;
- char *name;
char **dirs = (char**) directories;
_cleanup_close_ int fd = -1;
+ char *name;
int r;
assert(!strv_isempty(dirs));
@@ -222,24 +214,14 @@ int execute_directories(
* them to finish. Optionally a timeout is applied. If a file with the same name
* exists in more than one directory, the earliest one wins. */
- executor_pid = fork();
- if (executor_pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
-
- if (executor_pid == 0) {
+ r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0) {
r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
- r = wait_for_terminate_and_warn(name, executor_pid, true);
- if (r < 0)
- return log_error_errno(r, "Execution failed: %m");
- if (r > 0) {
- /* non-zero return code from child */
- log_error("Forker process failed.");
- return -EREMOTEIO;
- }
-
if (!callbacks)
return 0;
diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c
index a4a5c6b3f6..404361e8c1 100644
--- a/src/basic/fd-util.c
+++ b/src/basic/fd-util.c
@@ -192,9 +192,9 @@ int fd_cloexec(int fd, bool cloexec) {
}
void stdio_unset_cloexec(void) {
- fd_cloexec(STDIN_FILENO, false);
- fd_cloexec(STDOUT_FILENO, false);
- fd_cloexec(STDERR_FILENO, false);
+ (void) fd_cloexec(STDIN_FILENO, false);
+ (void) fd_cloexec(STDOUT_FILENO, false);
+ (void) fd_cloexec(STDERR_FILENO, false);
}
_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
@@ -227,20 +227,21 @@ int close_all_fds(const int except[], unsigned n_except) {
assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
for (fd = 3; fd < (int) rl.rlim_max; fd ++) {
+ int q;
if (fd_in_set(fd, except, n_except))
continue;
- if (close_nointr(fd) < 0)
- if (errno != EBADF && r == 0)
- r = -errno;
+ q = close_nointr(fd);
+ if (q < 0 && q != -EBADF && r >= 0)
+ r = q;
}
return r;
}
FOREACH_DIRENT(de, d, return -errno) {
- int fd = -1;
+ int fd = -1, q;
if (safe_atoi(de->d_name, &fd) < 0)
/* Let's better ignore this, just in case */
@@ -255,11 +256,9 @@ int close_all_fds(const int except[], unsigned n_except) {
if (fd_in_set(fd, except, n_except))
continue;
- if (close_nointr(fd) < 0) {
- /* Valgrind has its own FD and doesn't want to have it closed */
- if (errno != EBADF && r == 0)
- r = -errno;
- }
+ q = close_nointr(fd);
+ if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
+ r = q;
}
return r;
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 4e02d5b344..26d6174664 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -62,12 +62,30 @@ int write_string_stream_ts(
WriteStringFileFlags flags,
struct timespec *ts) {
+ bool needs_nl;
+
assert(f);
assert(line);
- fputs(line, f);
- if (!(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"))
- fputc('\n', f);
+ if (ferror(f))
+ return -EIO;
+
+ needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
+
+ if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
+ /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so
+ * that the write goes out in one go, instead of two */
+
+ line = strjoina(line, "\n");
+ needs_nl = false;
+ }
+
+ if (fputs(line, f) == EOF)
+ return -errno;
+
+ if (needs_nl)
+ if (fputc('\n', f) == EOF)
+ return -errno;
if (ts) {
struct timespec twice[2] = {*ts, *ts};
@@ -908,14 +926,16 @@ int write_env_file(const char *fname, char **l) {
}
int executable_is_script(const char *path, char **interpreter) {
- int r;
_cleanup_free_ char *line = NULL;
- int len;
+ size_t len;
char *ans;
+ int r;
assert(path);
r = read_one_line_file(path, &line);
+ if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */
+ return 0;
if (r < 0)
return r;
@@ -1205,8 +1225,7 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
if (!filename_is_valid(fn))
return -EINVAL;
- if (!extra)
- extra = "";
+ extra = strempty(extra);
t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
if (!t)
@@ -1239,8 +1258,7 @@ int tempfn_random(const char *p, const char *extra, char **ret) {
if (!filename_is_valid(fn))
return -EINVAL;
- if (!extra)
- extra = "";
+ extra = strempty(extra);
t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
if (!t)
@@ -1280,8 +1298,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
return r;
}
- if (!extra)
- extra = "";
+ extra = strempty(extra);
t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
if (!t)
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 4ca36faf09..a8e50d4c78 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -39,6 +39,7 @@
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
@@ -315,43 +316,60 @@ int fd_warn_permissions(const char *path, int fd) {
}
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
- _cleanup_close_ int fd;
- int r;
+ char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+ _cleanup_close_ int fd = -1;
+ int r, ret = 0;
assert(path);
- if (parents)
- mkdir_parents(path, 0755);
-
- fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
- IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
- if (fd < 0)
- return -errno;
+ /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink
+ * itself which is updated, not its target
+ *
+ * Returns the first error we encounter, but tries to apply as much as possible. */
- if (mode != MODE_INVALID) {
- r = fchmod(fd, mode);
- if (r < 0)
+ if (parents)
+ (void) mkdir_parents(path, 0755);
+
+ /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in
+ * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and
+ * won't trigger any driver magic or so. */
+ fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (fd < 0) {
+ if (errno != ENOENT)
return -errno;
- }
- if (uid != UID_INVALID || gid != GID_INVALID) {
- r = fchown(fd, uid, gid);
- if (r < 0)
+ /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file
+ * here, and nothing else */
+ fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
+ if (fd < 0)
return -errno;
}
+ /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
+ * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is
+ * something fchown(), fchmod(), futimensat() don't allow. */
+ xsprintf(fdpath, "/proc/self/fd/%i", fd);
+
+ if (mode != MODE_INVALID)
+ if (chmod(fdpath, mode) < 0)
+ ret = -errno;
+
+ if (uid_is_valid(uid) || gid_is_valid(gid))
+ if (chown(fdpath, uid, gid) < 0 && ret >= 0)
+ ret = -errno;
+
if (stamp != USEC_INFINITY) {
struct timespec ts[2];
timespec_store(&ts[0], stamp);
ts[1] = ts[0];
- r = futimens(fd, ts);
+ r = utimensat(AT_FDCWD, fdpath, ts, 0);
} else
- r = futimens(fd, NULL);
- if (r < 0)
+ r = utimensat(AT_FDCWD, fdpath, NULL, 0);
+ if (r < 0 && ret >= 0)
return -errno;
- return 0;
+ return ret;
}
int touch(const char *path) {
@@ -593,16 +611,35 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
return r;
}
+static bool safe_transition(const struct stat *a, const struct stat *b) {
+ /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
+ * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
+ * making us believe we read something safe even though it isn't safe in the specific context we open it in. */
+
+ if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
+ return true;
+
+ return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */
+}
+
int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
_cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
_cleanup_close_ int fd = -1;
unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
+ struct stat previous_stat;
bool exists = true;
char *todo;
int r;
assert(path);
+ /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
+ if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN))
+ return -EINVAL;
+
+ if (isempty(path))
+ return -EINVAL;
+
/* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
* symlinks relative to a root directory, instead of the root of the host.
*
@@ -623,13 +660,23 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
* function what to do when encountering a symlink with an absolute path as directory: prefix it by the
* specified path. */
+ /* A root directory of "/" or "" is identical to none */
+ if (isempty(original_root) || path_equal(original_root, "/"))
+ original_root = NULL;
+
if (original_root) {
r = path_make_absolute_cwd(original_root, &root);
if (r < 0)
return r;
- if (flags & CHASE_PREFIX_ROOT)
+ if (flags & CHASE_PREFIX_ROOT) {
+
+ /* We don't support relative paths in combination with a root directory */
+ if (!path_is_absolute(path))
+ return -EINVAL;
+
path = prefix_roota(root, path);
+ }
}
r = path_make_absolute_cwd(path, &buffer);
@@ -640,6 +687,11 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fd < 0)
return -errno;
+ if (flags & CHASE_SAFE) {
+ if (fstat(fd, &previous_stat) < 0)
+ return -errno;
+ }
+
todo = buffer;
for (;;) {
_cleanup_free_ char *first = NULL;
@@ -678,7 +730,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
/* Two dots? Then chop off the last bit of what we already found out. */
if (path_equal(first, "/..")) {
_cleanup_free_ char *parent = NULL;
- int fd_parent = -1;
+ _cleanup_close_ int fd_parent = -1;
/* If we already are at the top, then going up will not change anything. This is in-line with
* how the kernel handles this. */
@@ -701,8 +753,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fd_parent < 0)
return -errno;
+ if (flags & CHASE_SAFE) {
+ if (fstat(fd_parent, &st) < 0)
+ return -errno;
+
+ if (!safe_transition(&previous_stat, &st))
+ return -EPERM;
+
+ previous_stat = st;
+ }
+
safe_close(fd);
fd = fd_parent;
+ fd_parent = -1;
continue;
}
@@ -735,6 +798,12 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fstat(child, &st) < 0)
return -errno;
+ if ((flags & CHASE_SAFE) &&
+ !safe_transition(&previous_stat, &st))
+ return -EPERM;
+
+ previous_stat = st;
+
if ((flags & CHASE_NO_AUTOFS) &&
fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
return -EREMOTE;
@@ -765,6 +834,16 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fd < 0)
return -errno;
+ if (flags & CHASE_SAFE) {
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!safe_transition(&previous_stat, &st))
+ return -EPERM;
+
+ previous_stat = st;
+ }
+
free(done);
/* Note that we do not revalidate the root, we take it as is. */
@@ -821,6 +900,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
done = NULL;
}
+ if (flags & CHASE_OPEN) {
+ int q;
+
+ /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
+ * opening /proc/self/fd/xyz. */
+
+ assert(fd >= 0);
+ q = fd;
+ fd = -1;
+
+ return q;
+ }
+
return exists;
}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index a7ba61625d..4dba1ea56a 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -29,6 +29,7 @@
#include <unistd.h>
#include "time-util.h"
+#include "util.h"
int unlink_noerrno(const char *path);
@@ -80,22 +81,25 @@ union inotify_event_buffer {
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
enum {
- CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
- CHASE_NONEXISTENT = 2, /* If set, it's OK if the path doesn't actually exist. */
- CHASE_NO_AUTOFS = 4, /* If set, return -EREMOTE if autofs mount point found */
+ CHASE_PREFIX_ROOT = 1U << 0, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
+ CHASE_NONEXISTENT = 1U << 1, /* If set, it's OK if the path doesn't actually exist. */
+ CHASE_NO_AUTOFS = 1U << 2, /* If set, return -EREMOTE if autofs mount point found */
+ CHASE_SAFE = 1U << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
+ CHASE_OPEN = 1U << 4, /* If set, return an O_PATH object to the final component */
};
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
static inline void rmdir_and_free(char *p) {
+ PROTECT_ERRNO;
(void) rmdir(p);
free(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
static inline void unlink_and_free(char *p) {
- (void) unlink(p);
+ (void) unlink_noerrno(p);
free(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
diff --git a/src/shared/gcrypt-util.c b/src/basic/gcrypt-util.c
index 1bfb776725..1bfb776725 100644
--- a/src/shared/gcrypt-util.c
+++ b/src/basic/gcrypt-util.c
diff --git a/src/shared/gcrypt-util.h b/src/basic/gcrypt-util.h
index 69faf08e56..69faf08e56 100644
--- a/src/shared/gcrypt-util.h
+++ b/src/basic/gcrypt-util.h
diff --git a/src/basic/generate-af-list.sh b/src/basic/generate-af-list.sh
index 8d9cdd1836..fa74198e58 100755
--- a/src/basic/generate-af-list.sh
+++ b/src/basic/generate-af-list.sh
@@ -1,4 +1,5 @@
-#!/bin/sh -eu
+#!/bin/sh
+set -eu
$1 -E -dM -include sys/socket.h - </dev/null | \
grep -Ev 'AF_UNSPEC|AF_MAX' | \
diff --git a/src/basic/generate-arphrd-list.sh b/src/basic/generate-arphrd-list.sh
index ee207fb38e..e4e7271c4d 100755
--- a/src/basic/generate-arphrd-list.sh
+++ b/src/basic/generate-arphrd-list.sh
@@ -1,4 +1,5 @@
-#!/bin/sh -eu
+#!/bin/sh
+set -eu
$1 -dM -include net/if_arp.h - </dev/null | \
awk '/^#define[ \t]+ARPHRD_[^ \t]+[ \t]+[^ \t]/ { print $2; }' | \
diff --git a/src/basic/generate-cap-list.sh b/src/basic/generate-cap-list.sh
index 1d4a562e7c..0628d2425f 100755
--- a/src/basic/generate-cap-list.sh
+++ b/src/basic/generate-cap-list.sh
@@ -1,4 +1,5 @@
-#!/bin/sh -eu
+#!/bin/sh
+set -eu
$1 -dM -include linux/capability.h -include "$2" -include "$3" - </dev/null | \
awk '/^#define[ \t]+CAP_[A-Z_]+[ \t]+/ { print $2; }' | \
diff --git a/src/basic/generate-errno-list.sh b/src/basic/generate-errno-list.sh
index e2bab8b320..953d5e37b8 100755
--- a/src/basic/generate-errno-list.sh
+++ b/src/basic/generate-errno-list.sh
@@ -1,4 +1,5 @@
-#!/bin/sh -eu
+#!/bin/sh
+set -eu
$1 -dM -include errno.h - </dev/null | \
awk '/^#define[ \t]+E[^ _]+[ \t]+/ { print $2; }'
diff --git a/src/basic/generate-socket-protocol-list.sh b/src/basic/generate-socket-protocol-list.sh
new file mode 100755
index 0000000000..a9b1e0fb57
--- /dev/null
+++ b/src/basic/generate-socket-protocol-list.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -eu
+
+$1 -dM -include netinet/in.h - </dev/null | \
+ awk '/^#define[ \t]+IPPROTO_[^ \t]+[ \t]+[^ \t]/ { print $2; }' | \
+ sed -e 's/IPPROTO_//'
diff --git a/src/basic/gunicode.c b/src/basic/gunicode.c
index e6ac0545a4..8aff4a0fc5 100644
--- a/src/basic/gunicode.c
+++ b/src/basic/gunicode.c
@@ -4,8 +4,6 @@
* Copyright 2000, 2005 Red Hat, Inc.
*/
-#include <stdlib.h>
-
#include "gunicode.h"
#define unichar uint32_t
diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c
index e69f81558d..5267758769 100644
--- a/src/basic/hash-funcs.c
+++ b/src/basic/hash-funcs.c
@@ -19,6 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <string.h>
+
#include "hash-funcs.h"
void string_hash_func(const void *p, struct siphash *state) {
diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h
index d837d6f28c..edae52e3db 100644
--- a/src/basic/hostname-util.h
+++ b/src/basic/hostname-util.h
@@ -21,6 +21,7 @@
***/
#include <stdbool.h>
+#include <stdio.h>
#include "macro.h"
diff --git a/src/basic/io-util.c b/src/basic/io-util.c
index 77c9bdc739..08ad42ed95 100644
--- a/src/basic/io-util.c
+++ b/src/basic/io-util.c
@@ -33,6 +33,7 @@ int flush_fd(int fd) {
.fd = fd,
.events = POLLIN,
};
+ int count = 0;
/* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything
* read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read
@@ -52,7 +53,7 @@ int flush_fd(int fd) {
return -errno;
} else if (r == 0)
- return 0;
+ return count;
l = read(fd, buf, sizeof(buf));
if (l < 0) {
@@ -61,11 +62,13 @@ int flush_fd(int fd) {
continue;
if (errno == EAGAIN)
- return 0;
+ return count;
return -errno;
} else if (l == 0)
- return 0;
+ return count;
+
+ count += (int) l;
}
}
diff --git a/src/basic/journal-importer.c b/src/basic/journal-importer.c
index 6942c370cb..11054589e3 100644
--- a/src/basic/journal-importer.c
+++ b/src/basic/journal-importer.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <unistd.h>
#include "alloc-util.h"
diff --git a/src/basic/label.c b/src/basic/label.c
index ce81d286ab..18c9a23fea 100644
--- a/src/basic/label.c
+++ b/src/basic/label.c
@@ -22,6 +22,7 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "btrfs-util.h"
#include "label.h"
#include "macro.h"
#include "selinux-util.h"
@@ -41,16 +42,17 @@ int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
return 0;
}
-int mkdir_label(const char *path, mode_t mode) {
+int symlink_label(const char *old_path, const char *new_path) {
int r;
- assert(path);
+ assert(old_path);
+ assert(new_path);
- r = mac_selinux_create_file_prepare(path, S_IFDIR);
+ r = mac_selinux_create_file_prepare(new_path, S_IFLNK);
if (r < 0)
return r;
- if (mkdir(path, mode) < 0)
+ if (symlink(old_path, new_path) < 0)
r = -errno;
mac_selinux_create_file_clear();
@@ -58,26 +60,23 @@ int mkdir_label(const char *path, mode_t mode) {
if (r < 0)
return r;
- return mac_smack_fix(path, false, false);
+ return mac_smack_fix(new_path, false, false);
}
-int symlink_label(const char *old_path, const char *new_path) {
+int btrfs_subvol_make_label(const char *path) {
int r;
- assert(old_path);
- assert(new_path);
+ assert(path);
- r = mac_selinux_create_file_prepare(new_path, S_IFLNK);
+ r = mac_selinux_create_file_prepare(path, S_IFDIR);
if (r < 0)
return r;
- if (symlink(old_path, new_path) < 0)
- r = -errno;
-
+ r = btrfs_subvol_make(path);
mac_selinux_create_file_clear();
if (r < 0)
return r;
- return mac_smack_fix(new_path, false, false);
+ return mac_smack_fix(path, false, false);
}
diff --git a/src/basic/label.h b/src/basic/label.h
index 86447c2e98..d73dacec4f 100644
--- a/src/basic/label.h
+++ b/src/basic/label.h
@@ -27,3 +27,5 @@ int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
int mkdir_label(const char *path, mode_t mode);
int symlink_label(const char *old_path, const char *new_path);
+
+int btrfs_subvol_make_label(const char *path);
diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h
index 60ce017a15..f75dcbc3d1 100644
--- a/src/basic/locale-util.h
+++ b/src/basic/locale-util.h
@@ -22,6 +22,7 @@
#include <libintl.h>
#include <stdbool.h>
+#include <locale.h>
#include "macro.h"
@@ -75,3 +76,10 @@ LocaleVariable locale_variable_from_string(const char *s) _pure_;
int get_keymaps(char ***l);
bool keymap_is_valid(const char *name);
+
+static inline void freelocalep(locale_t *p) {
+ if (*p == (locale_t) 0)
+ return;
+
+ freelocale(*p);
+}
diff --git a/src/basic/log.c b/src/basic/log.c
index 20d9588e2f..12ee65a5c3 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -54,6 +54,7 @@
#include "syslog-util.h"
#include "terminal-util.h"
#include "time-util.h"
+#include "utf8.h"
#include "util.h"
#define SNDBUF_SIZE (8*1024*1024)
@@ -76,40 +77,40 @@ static bool show_location = false;
static bool upgrade_syslog_to_journal = false;
static bool always_reopen_console = false;
static bool open_when_needed = false;
+static bool prohibit_ipc = false;
/* Akin to glibc's __abort_msg; which is private and we hence cannot
* use here. */
static char *log_abort_msg = NULL;
-void log_close_console(void) {
+static void log_close_console(void) {
if (console_fd < 0)
return;
- if (getpid_cached() == 1) {
- if (console_fd >= 3)
- safe_close(console_fd);
+ if (console_fd >= 3)
+ safe_close(console_fd);
- console_fd = -1;
- }
+ console_fd = -1;
}
static int log_open_console(void) {
- if (console_fd >= 0)
+ if (!always_reopen_console) {
+ console_fd = STDERR_FILENO;
return 0;
+ }
- if (always_reopen_console) {
+ if (console_fd < 3) {
console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
if (console_fd < 0)
return console_fd;
- } else
- console_fd = STDERR_FILENO;
+ }
return 0;
}
-void log_close_kmsg(void) {
+static void log_close_kmsg(void) {
kmsg_fd = safe_close(kmsg_fd);
}
@@ -125,7 +126,7 @@ static int log_open_kmsg(void) {
return 0;
}
-void log_close_syslog(void) {
+static void log_close_syslog(void) {
syslog_fd = safe_close(syslog_fd);
}
@@ -197,7 +198,7 @@ fail:
return r;
}
-void log_close_journal(void) {
+static void log_close_journal(void) {
journal_fd = safe_close(journal_fd);
}
@@ -239,7 +240,8 @@ int log_open(void) {
/* If we don't use the console we close it here, to not get
* killed by SAK. If we don't use syslog we close it here so
* that we are not confused by somebody deleting the socket in
- * the fs. If we don't use /dev/kmsg we still keep it open,
+ * the fs, and to make sure we don't use it if prohibit_ipc is
+ * set. If we don't use /dev/kmsg we still keep it open,
* because there is no reason to close it. */
if (log_target == LOG_TARGET_NULL) {
@@ -249,11 +251,12 @@ int log_open(void) {
return 0;
}
- if (!IN_SET(log_target, LOG_TARGET_AUTO, LOG_TARGET_SAFE) ||
+ if (log_target != LOG_TARGET_AUTO ||
getpid_cached() == 1 ||
isatty(STDERR_FILENO) <= 0) {
- if (IN_SET(log_target, LOG_TARGET_AUTO,
+ if (!prohibit_ipc &&
+ IN_SET(log_target, LOG_TARGET_AUTO,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_JOURNAL)) {
r = log_open_journal();
@@ -264,7 +267,8 @@ int log_open(void) {
}
}
- if (IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG,
+ if (!prohibit_ipc &&
+ IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_SYSLOG)) {
r = log_open_syslog();
if (r >= 0) {
@@ -275,7 +279,6 @@ int log_open(void) {
}
if (IN_SET(log_target, LOG_TARGET_AUTO,
- LOG_TARGET_SAFE,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_KMSG)) {
@@ -627,7 +630,6 @@ int log_dispatch_internal(
if (k <= 0 &&
IN_SET(log_target, LOG_TARGET_AUTO,
- LOG_TARGET_SAFE,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_KMSG)) {
@@ -1219,17 +1221,18 @@ static const char *const log_target_table[_LOG_TARGET_MAX] = {
[LOG_TARGET_SYSLOG] = "syslog",
[LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
[LOG_TARGET_AUTO] = "auto",
- [LOG_TARGET_SAFE] = "safe",
- [LOG_TARGET_NULL] = "null"
+ [LOG_TARGET_NULL] = "null",
};
DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
void log_received_signal(int level, const struct signalfd_siginfo *si) {
- if (si->ssi_pid > 0) {
+ assert(si);
+
+ if (pid_is_valid(si->ssi_pid)) {
_cleanup_free_ char *p = NULL;
- get_process_comm(si->ssi_pid, &p);
+ (void) get_process_comm(si->ssi_pid, &p);
log_full(level,
"Received SIG%s from PID %"PRIu32" (%s).",
@@ -1239,7 +1242,6 @@ void log_received_signal(int level, const struct signalfd_siginfo *si) {
log_full(level,
"Received SIG%s.",
signal_to_string(si->ssi_signo));
-
}
int log_syntax_internal(
@@ -1289,8 +1291,37 @@ int log_syntax_internal(
NULL);
}
+int log_syntax_invalid_utf8_internal(
+ const char *unit,
+ int level,
+ const char *config_file,
+ unsigned config_line,
+ const char *file,
+ int line,
+ const char *func,
+ const char *rvalue) {
+
+ _cleanup_free_ char *p = NULL;
+
+ if (rvalue)
+ p = utf8_escape_invalid(rvalue);
+
+ log_syntax_internal(unit, level, config_file, config_line, 0, file, line, func,
+ "String is not UTF-8 clean, ignoring assignment: %s", strna(p));
+
+ return -EINVAL;
+}
+
void log_set_upgrade_syslog_to_journal(bool b) {
upgrade_syslog_to_journal = b;
+
+ /* Make the change effective immediately */
+ if (b) {
+ if (log_target == LOG_TARGET_SYSLOG)
+ log_target = LOG_TARGET_JOURNAL;
+ else if (log_target == LOG_TARGET_SYSLOG_OR_KMSG)
+ log_target = LOG_TARGET_JOURNAL_OR_KMSG;
+ }
}
void log_set_always_reopen_console(bool b) {
@@ -1300,3 +1331,14 @@ void log_set_always_reopen_console(bool b) {
void log_set_open_when_needed(bool b) {
open_when_needed = b;
}
+
+void log_set_prohibit_ipc(bool b) {
+ prohibit_ipc = b;
+}
+
+int log_emergency_level(void) {
+ /* Returns the log level to use for log_emergency() logging. We use LOG_EMERG only when we are PID 1, as only
+ * then the system of the whole system is obviously affected. */
+
+ return getpid_cached() == 1 ? LOG_EMERG : LOG_ERR;
+}
diff --git a/src/basic/log.h b/src/basic/log.h
index aa5976c3c0..5b5a25bd6d 100644
--- a/src/basic/log.h
+++ b/src/basic/log.h
@@ -20,18 +20,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <sys/signalfd.h>
-#include <sys/socket.h>
#include <syslog.h>
-#include "sd-id128.h"
-
#include "macro.h"
-#include "process-util.h"
+
+/* Some structures we reference but don't want to pull in headers for */
+struct iovec;
+struct signalfd_siginfo;
typedef enum LogRealm {
LOG_REALM_SYSTEMD,
@@ -52,7 +50,6 @@ typedef enum LogTarget{
LOG_TARGET_SYSLOG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
- LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */
LOG_TARGET_NULL,
_LOG_TARGET_MAX,
_LOG_TARGET_INVALID = -1
@@ -97,11 +94,6 @@ int log_open(void);
void log_close(void);
void log_forget_fds(void);
-void log_close_syslog(void);
-void log_close_journal(void);
-void log_close_kmsg(void);
-void log_close_console(void);
-
void log_parse_environment_realm(LogRealm realm);
#define log_parse_environment() \
log_parse_environment_realm(LOG_REALM)
@@ -194,7 +186,7 @@ int log_struct_iovec_internal(
const char *file,
int line,
const char *func,
- const struct iovec input_iovec[],
+ const struct iovec *input_iovec,
size_t n_input_iovec);
/* This modifies the buffer passed! */
@@ -240,9 +232,9 @@ void log_assert_failed_return_realm(
/* Logging with level */
#define log_full_errno_realm(realm, level, error, ...) \
({ \
- int _level = (level), _e = (error); \
- (log_get_max_level_realm((realm)) >= LOG_PRI(_level)) \
- ? log_internal_realm(LOG_REALM_PLUS_LEVEL((realm), _level), _e, \
+ int _level = (level), _e = (error), _realm = (realm); \
+ (log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \
+ ? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \
__FILE__, __LINE__, __func__, __VA_ARGS__) \
: -abs(_e); \
})
@@ -252,13 +244,15 @@ void log_assert_failed_return_realm(
#define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__)
+int log_emergency_level(void);
+
/* Normal logging */
#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
-#define log_emergency(...) log_full(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__)
+#define log_emergency(...) log_full(log_emergency_level(), __VA_ARGS__)
/* Logging triggered by an errno-like error */
#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__)
@@ -266,7 +260,7 @@ void log_assert_failed_return_realm(
#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__)
#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__)
#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__)
-#define log_emergency_errno(error, ...) log_full_errno(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__)
+#define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__)
#ifdef LOG_TRACE
# define log_trace(...) log_debug(__VA_ARGS__)
@@ -302,10 +296,20 @@ LogTarget log_target_from_string(const char *s) _pure_;
void log_received_signal(int level, const struct signalfd_siginfo *si);
+/* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */
void log_set_upgrade_syslog_to_journal(bool b);
+
+/* If turned on, and log_open() is called, we'll not use STDERR_FILENO for logging ever, but rather open /dev/console */
void log_set_always_reopen_console(bool b);
+
+/* If turned on, we'll open the log stream implicitly if needed on each individual log call. This is normally not
+ * desired as we want to reuse our logging streams. It is useful however */
void log_set_open_when_needed(bool b);
+/* If turned on, then we'll never use IPC-based logging, i.e. never log to syslog or the journal. We'll only log to
+ * stderr, the console or kmsg */
+void log_set_prohibit_ipc(bool b);
+
int log_syntax_internal(
const char *unit,
int level,
@@ -317,6 +321,16 @@ int log_syntax_internal(
const char *func,
const char *format, ...) _printf_(9, 10);
+int log_syntax_invalid_utf8_internal(
+ const char *unit,
+ int level,
+ const char *config_file,
+ unsigned config_line,
+ const char *file,
+ int line,
+ const char *func,
+ const char *rvalue);
+
#define log_syntax(unit, level, config_file, config_line, error, ...) \
({ \
int _level = (level), _e = (error); \
@@ -328,10 +342,9 @@ int log_syntax_internal(
#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \
({ \
int _level = (level); \
- if (log_get_max_level() >= LOG_PRI(_level)) { \
- _cleanup_free_ char *_p = NULL; \
- _p = utf8_escape_invalid(rvalue); \
- log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \
- "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \
- } \
+ (log_get_max_level() >= LOG_PRI(_level)) \
+ ? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, __FILE__, __LINE__, __func__, rvalue) \
+ : -EINVAL; \
})
+
+#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
diff --git a/src/basic/macro.h b/src/basic/macro.h
index 02d22de833..89bdd852a9 100644
--- a/src/basic/macro.h
+++ b/src/basic/macro.h
@@ -139,11 +139,17 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
}
+#ifndef __COVERITY__
+# define VOID_0 ((void)0)
+#else
+# define VOID_0 ((void*)0)
+#endif
+
#define ELEMENTSOF(x) \
__extension__ (__builtin_choose_expr( \
!__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
sizeof(x)/sizeof((x)[0]), \
- (void)0))
+ VOID_0))
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
@@ -181,7 +187,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
__builtin_constant_p(_B) && \
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
((_A) > (_B)) ? (_A) : (_B), \
- (void)0))
+ VOID_0))
/* takes two types and returns the size of the larger one */
#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
diff --git a/src/basic/meson.build b/src/basic/meson.build
index a37e279e57..44cd31ecbe 100644
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -15,7 +15,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with systemd; If not, see <http://www.gnu.org/licenses/>.
-basic_sources_plain = files('''
+basic_sources = files('''
MurmurHash2.c
MurmurHash2.h
af-list.c
@@ -35,6 +35,8 @@ basic_sources_plain = files('''
bitmap.c
bitmap.h
blkid-util.h
+ blockdev-util.c
+ blockdev-util.h
bpf-program.c
bpf-program.h
btrfs-ctree.h
@@ -148,6 +150,8 @@ basic_sources_plain = files('''
proc-cmdline.h
process-util.c
process-util.h
+ procfs-util.c
+ procfs-util.h
random-util.c
random-util.h
ratelimit.c
@@ -176,6 +180,8 @@ basic_sources_plain = files('''
smack-util.c
smack-util.h
socket-label.c
+ socket-protocol-list.c
+ socket-protocol-list.h
socket-util.c
socket-util.h
sparse-endian.h
@@ -201,10 +207,10 @@ basic_sources_plain = files('''
time-util.h
umask-util.h
unaligned.h
- unit-name.c
- unit-name.h
unit-def.c
unit-def.h
+ unit-name.c
+ unit-name.h
user-util.c
user-util.h
utf8.c
@@ -255,11 +261,19 @@ errno_list_txt = custom_target(
command : [generate_errno_list, cpp],
capture : true)
+generate_socket_protocol_list = find_program('generate-socket-protocol-list.sh')
+socket_protocol_list_txt = custom_target(
+ 'socket-protocol-list.txt',
+ output : 'socket-protocol-list.txt',
+ command : [generate_socket_protocol_list, cpp],
+ capture : true)
+
generated_gperf_headers = []
foreach item : [['af', af_list_txt, 'af', ''],
['arphrd', arphrd_list_txt, 'arphrd', 'ARPHRD_'],
['cap', cap_list_txt, 'capability', ''],
- ['errno', errno_list_txt, 'errno', '']]
+ ['errno', errno_list_txt, 'errno', ''],
+ ['socket-protocol', socket_protocol_list_txt, 'socket_protocol', 'IPPROTO_']]
fname = '@0@-from-name.gperf'.format(item[0])
gperf_file = custom_target(
@@ -294,7 +308,7 @@ foreach item : [['af', af_list_txt, 'af', ''],
generated_gperf_headers += [target1, target2]
endforeach
-basic_sources = basic_sources_plain + [missing_h] + generated_gperf_headers
+basic_sources += [missing_h] + generated_gperf_headers
libbasic = static_library(
'basic',
@@ -303,6 +317,16 @@ libbasic = static_library(
dependencies : [threads,
libcap,
libblkid,
- libselinux,
- ],
+ libselinux],
+ c_args : ['-fvisibility=default'],
install : false)
+
+# A convenience library that is separate from libbasic to avoid
+# unnecessary linking to libgcrypt.
+libbasic_gcrypt = static_library(
+ 'basic-gcrypt',
+ 'gcrypt-util.c',
+ 'gcrypt-util.h',
+ include_directories : includes,
+ dependencies : [libgcrypt],
+ c_args : ['-fvisibility=default'])
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 790f9f55a5..1280e6c410 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -206,6 +206,32 @@ struct sockaddr_vm {
#endif
#if ! HAVE_LINUX_BTRFS_H
+#define BTRFS_IOC_QGROUP_ASSIGN _IOW(BTRFS_IOCTL_MAGIC, 41, \
+ struct btrfs_ioctl_qgroup_assign_args)
+#define BTRFS_IOC_QGROUP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 42, \
+ struct btrfs_ioctl_qgroup_create_args)
+#define BTRFS_IOC_QUOTA_RESCAN _IOW(BTRFS_IOCTL_MAGIC, 44, \
+ struct btrfs_ioctl_quota_rescan_args)
+#define BTRFS_IOC_QUOTA_RESCAN_STATUS _IOR(BTRFS_IOCTL_MAGIC, 45, \
+ struct btrfs_ioctl_quota_rescan_args)
+
+struct btrfs_ioctl_quota_rescan_args {
+ __u64 flags;
+ __u64 progress;
+ __u64 reserved[6];
+};
+
+struct btrfs_ioctl_qgroup_assign_args {
+ __u64 assign;
+ __u64 src;
+ __u64 dst;
+};
+
+struct btrfs_ioctl_qgroup_create_args {
+ __u64 create;
+ __u64 qgroupid;
+};
+
struct btrfs_ioctl_vol_args {
int64_t fd;
char name[BTRFS_PATH_NAME_MAX + 1];
@@ -543,6 +569,38 @@ struct btrfs_ioctl_quota_ctl_args {
#define PR_SET_CHILD_SUBREAPER 36
#endif
+#ifndef PR_SET_MM_ARG_START
+#define PR_SET_MM_ARG_START 8
+#endif
+
+#ifndef PR_SET_MM_ARG_END
+#define PR_SET_MM_ARG_END 9
+#endif
+
+#ifndef PR_SET_MM_ENV_START
+#define PR_SET_MM_ENV_START 10
+#endif
+
+#ifndef PR_SET_MM_ENV_END
+#define PR_SET_MM_ENV_END 11
+#endif
+
+#ifndef EFIVARFS_MAGIC
+#define EFIVARFS_MAGIC 0xde5e81e4
+#endif
+
+#ifndef SMACK_MAGIC
+#define SMACK_MAGIC 0x43415d53
+#endif
+
+#ifndef DM_DEFERRED_REMOVE
+#define DM_DEFERRED_REMOVE (1 << 17)
+#endif
+
+#ifndef MAX_HANDLE_SZ
+#define MAX_HANDLE_SZ 128
+#endif
+
#if ! HAVE_SECURE_GETENV
# if HAVE___SECURE_GETENV
# define secure_getenv __secure_getenv
@@ -563,6 +621,10 @@ struct btrfs_ioctl_quota_ctl_args {
# define SO_REUSEPORT 15
#endif
+#ifndef SO_PEERGROUPS
+# define SO_PEERGROUPS 59
+#endif
+
#ifndef EVIOCREVOKE
# define EVIOCREVOKE _IOW('E', 0x91, int)
#endif
@@ -653,18 +715,28 @@ struct input_mask {
#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1)
#endif
-#if !HAVE_IFLA_IPVLAN_MODE
+#if !HAVE_IFLA_IPVLAN_FLAGS
#define IFLA_IPVLAN_UNSPEC 0
#define IFLA_IPVLAN_MODE 1
-#define __IFLA_IPVLAN_MAX 2
+#define IFLA_IPVLAN_FLAGS 2
+#define __IFLA_IPVLAN_MAX 3
#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
#define IPVLAN_MODE_L2 0
#define IPVLAN_MODE_L3 1
+#define IPVLAN_MODE_L3S 2
#define IPVLAN_MAX 2
#endif
+#if !HAVE_IPVLAN_F_PRIVATE
+#define IPVLAN_F_PRIVATE 0x01
+#define IPVLAN_F_VEPA 0x02
+#define __IPVLAN_F_PRIVATE_MAX 3
+
+#define HAVE_IPVLAN_F_PRIVATE_MAX (__HAVE_IPVLAN_F_PRIVATE_MAX - 1)
+#endif
+
#if !HAVE_IFLA_VTI_REMOTE
#define IFLA_VTI_UNSPEC 0
#define IFLA_VTI_LINK 1
diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h
index fd82c11e94..c938d0d976 100644
--- a/src/basic/missing_syscall.h
+++ b/src/basic/missing_syscall.h
@@ -330,10 +330,14 @@ static inline ssize_t copy_file_range(int fd_in, loff_t *off_in,
# define __NR_bpf 321
# elif defined __aarch64__
# define __NR_bpf 280
+# elif defined __arm__
+# define __NR_bpf 386
# elif defined __sparc__
# define __NR_bpf 349
# elif defined __s390__
# define __NR_bpf 351
+# elif defined __tilegx__
+# define __NR_bpf 280
# else
# warning "__NR_bpf not defined for your architecture"
# endif
diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c
index 16eb7ce265..6f3a46f467 100644
--- a/src/basic/mkdir-label.c
+++ b/src/basic/mkdir-label.c
@@ -20,11 +20,32 @@
***/
#include <stdio.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "label.h"
+#include "macro.h"
#include "mkdir.h"
+#include "selinux-util.h"
+#include "smack-util.h"
+
+int mkdir_label(const char *path, mode_t mode) {
+ int r;
+
+ assert(path);
+
+ r = mac_selinux_create_file_prepare(path, S_IFDIR);
+ if (r < 0)
+ return r;
+
+ r = mkdir_errno_wrapper(path, mode);
+ mac_selinux_create_file_clear();
+ if (r < 0)
+ return r;
+
+ return mac_smack_fix(path, false, false);
+}
int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) {
return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir_label);
diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c
index 4386b38c4a..de4746c867 100644
--- a/src/basic/mkdir.c
+++ b/src/basic/mkdir.c
@@ -35,6 +35,8 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, boo
struct stat st;
int r;
+ assert(_mkdir != mkdir);
+
if (_mkdir(path, mode) >= 0) {
r = chmod_and_chown(path, mode, uid, gid);
if (r < 0)
@@ -68,8 +70,14 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, boo
return 0;
}
+int mkdir_errno_wrapper(const char *pathname, mode_t mode) {
+ if (mkdir(pathname, mode) < 0)
+ return -errno;
+ return 0;
+}
+
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) {
- return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir);
+ return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir_errno_wrapper);
}
int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
@@ -77,6 +85,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mk
int r;
assert(path);
+ assert(_mkdir != mkdir);
if (prefix && !path_startswith(path, prefix))
return -ENOTDIR;
@@ -104,8 +113,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mk
e = p + strcspn(p, "/");
p = e + strspn(e, "/");
- /* Is this the last component? If so, then we're
- * done */
+ /* Is this the last component? If so, then we're done */
if (*p == 0)
return 0;
@@ -116,13 +124,13 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mk
continue;
r = _mkdir(t, mode);
- if (r < 0 && errno != EEXIST)
- return -errno;
+ if (r < 0 && r != -EEXIST)
+ return r;
}
}
int mkdir_parents(const char *path, mode_t mode) {
- return mkdir_parents_internal(NULL, path, mode, mkdir);
+ return mkdir_parents_internal(NULL, path, mode, mkdir_errno_wrapper);
}
int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
@@ -130,17 +138,19 @@ int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_fu
/* Like mkdir -p */
+ assert(_mkdir != mkdir);
+
r = mkdir_parents_internal(prefix, path, mode, _mkdir);
if (r < 0)
return r;
r = _mkdir(path, mode);
- if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0))
- return -errno;
+ if (r < 0 && (r != -EEXIST || is_dir(path, true) <= 0))
+ return r;
return 0;
}
int mkdir_p(const char *path, mode_t mode) {
- return mkdir_p_internal(NULL, path, mode, mkdir);
+ return mkdir_p_internal(NULL, path, mode, mkdir_errno_wrapper);
}
diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h
index 04a537f8a8..d6c2d579a3 100644
--- a/src/basic/mkdir.h
+++ b/src/basic/mkdir.h
@@ -23,6 +23,7 @@
#include <sys/types.h>
+int mkdir_errno_wrapper(const char *pathname, mode_t mode);
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink);
int mkdir_parents(const char *path, mode_t mode);
int mkdir_p(const char *path, mode_t mode);
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index d03f60e01a..2c22753dea 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -28,6 +28,7 @@
#include "alloc-util.h"
#include "errno-list.h"
#include "extract-word.h"
+#include "locale-util.h"
#include "macro.h"
#include "parse-util.h"
#include "process-util.h"
@@ -83,7 +84,7 @@ int parse_mode(const char *s, mode_t *ret) {
l = strtol(s, &x, 8);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (l < 0 || l > 07777)
return -ERANGE;
@@ -283,7 +284,8 @@ int parse_errno(const char *t) {
if (r < 0)
return r;
- if (e < 0 || e > ERRNO_MAX)
+ /* 0 is also allowed here */
+ if (!errno_is_valid(e) && e != 0)
return -ERANGE;
return e;
@@ -390,7 +392,7 @@ int safe_atou(const char *s, unsigned *ret_u) {
l = strtoul(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
@@ -412,7 +414,7 @@ int safe_atoi(const char *s, int *ret_i) {
l = strtol(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if ((long) (int) l != l)
return -ERANGE;
@@ -434,7 +436,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
l = strtoull(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (*s == '-')
return -ERANGE;
@@ -454,7 +456,7 @@ int safe_atolli(const char *s, long long int *ret_lli) {
l = strtoll(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
*ret_lli = l;
@@ -474,7 +476,7 @@ int safe_atou8(const char *s, uint8_t *ret) {
l = strtoul(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
@@ -498,7 +500,7 @@ int safe_atou16(const char *s, uint16_t *ret) {
l = strtoul(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
@@ -520,7 +522,7 @@ int safe_atoi16(const char *s, int16_t *ret) {
l = strtol(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if ((long) (int16_t) l != l)
return -ERANGE;
@@ -530,9 +532,9 @@ int safe_atoi16(const char *s, int16_t *ret) {
}
int safe_atod(const char *s, double *ret_d) {
+ _cleanup_(freelocalep) locale_t loc = (locale_t) 0;
char *x = NULL;
double d = 0;
- locale_t loc;
assert(s);
assert(ret_d);
@@ -543,16 +545,11 @@ int safe_atod(const char *s, double *ret_d) {
errno = 0;
d = strtod_l(s, &x, loc);
- if (errno > 0) {
- freelocale(loc);
+ if (errno > 0)
return -errno;
- }
- if (!x || x == s || *x) {
- freelocale(loc);
+ if (!x || x == s || *x != 0)
return -EINVAL;
- }
- freelocale(loc);
*ret_d = (double) d;
return 0;
}
@@ -595,19 +592,20 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
int parse_percent_unbounded(const char *p) {
const char *pc, *n;
- unsigned v;
- int r;
+ int r, v;
pc = endswith(p, "%");
if (!pc)
return -EINVAL;
n = strndupa(p, pc - p);
- r = safe_atou(n, &v);
+ r = safe_atoi(n, &v);
if (r < 0)
return r;
+ if (v < 0)
+ return -ERANGE;
- return (int) v;
+ return v;
}
int parse_percent(const char *p) {
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index ab4778d4ed..df94629385 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -81,14 +81,36 @@ char *path_make_absolute(const char *p, const char *prefix) {
/* Makes every item in the list an absolute path by prepending
* the prefix, if specified and necessary */
- if (path_is_absolute(p) || !prefix)
+ if (path_is_absolute(p) || isempty(prefix))
return strdup(p);
- return strjoin(prefix, "/", p);
+ if (endswith(prefix, "/"))
+ return strjoin(prefix, p);
+ else
+ return strjoin(prefix, "/", p);
+}
+
+int safe_getcwd(char **ret) {
+ char *cwd;
+
+ cwd = get_current_dir_name();
+ if (!cwd)
+ return negative_errno();
+
+ /* Let's make sure the directory is really absolute, to protect us from the logic behind
+ * CVE-2018-1000001 */
+ if (cwd[0] != '/') {
+ free(cwd);
+ return -ENOMEDIUM;
+ }
+
+ *ret = cwd;
+ return 0;
}
int path_make_absolute_cwd(const char *p, char **ret) {
char *c;
+ int r;
assert(p);
assert(ret);
@@ -101,11 +123,14 @@ int path_make_absolute_cwd(const char *p, char **ret) {
else {
_cleanup_free_ char *cwd = NULL;
- cwd = get_current_dir_name();
- if (!cwd)
- return negative_errno();
+ r = safe_getcwd(&cwd);
+ if (r < 0)
+ return r;
- c = strjoin(cwd, "/", p);
+ if (endswith(cwd, "/"))
+ c = strjoin(cwd, p);
+ else
+ c = strjoin(cwd, "/", p);
}
if (!c)
return -ENOMEM;
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index f79cdf928e..89c285e076 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -41,6 +41,7 @@ bool is_path(const char *p) _pure_;
int path_split_and_make_absolute(const char *p, char ***ret);
bool path_is_absolute(const char *p) _pure_;
char* path_make_absolute(const char *p, const char *prefix);
+int safe_getcwd(char **ret);
int path_make_absolute_cwd(const char *p, char **ret);
int path_make_relative(const char *from_dir, const char *to_path, char **_r);
char* path_kill_slashes(char *path);
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 17c94f44a0..dc7c9ef9ef 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -56,6 +56,7 @@
#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "terminal-util.h"
#include "user-util.h"
#include "util.h"
@@ -296,10 +297,17 @@ int rename_process(const char name[]) {
if (isempty(name))
return -EINVAL; /* let's not confuse users unnecessarily with an empty name */
+ if (!is_main_thread())
+ return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we
+ * cache things without locking, and we make assumptions that PR_SET_NAME sets the
+ * process name that isn't correct on any other threads */
+
l = strlen(name);
- /* First step, change the comm field. */
- (void) prctl(PR_SET_NAME, name);
+ /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we
+ * can use PR_SET_NAME, which sets the thread name for the calling thread. */
+ if (prctl(PR_SET_NAME, name) < 0)
+ log_debug_errno(errno, "PR_SET_NAME failed: %m");
if (l > 15) /* Linux process names can be 15 chars at max */
truncated = true;
@@ -679,32 +687,43 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) {
* A warning is emitted if the process terminates abnormally,
* and also if it returns non-zero unless check_exit_code is true.
*/
-int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
- int r;
+int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
+ _cleanup_free_ char *buffer = NULL;
siginfo_t status;
+ int r, prio;
- assert(name);
assert(pid > 1);
+ if (!name) {
+ r = get_process_comm(pid, &buffer);
+ if (r < 0)
+ log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
+ else
+ name = buffer;
+ }
+
+ prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG;
+
r = wait_for_terminate(pid, &status);
if (r < 0)
- return log_warning_errno(r, "Failed to wait for %s: %m", name);
+ return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name));
if (status.si_code == CLD_EXITED) {
- if (status.si_status != 0)
- log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
- "%s failed with error code %i.", name, status.si_status);
+ if (status.si_status != EXIT_SUCCESS)
+ log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG,
+ "%s failed with exit status %i.", strna(name), status.si_status);
else
log_debug("%s succeeded.", name);
return status.si_status;
+
} else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) {
- log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
+ log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status));
return -EPROTO;
}
- log_warning("%s failed due to unknown reason.", name);
+ log_full(prio, "%s failed due to unknown reason.", strna(name));
return -EPROTO;
}
@@ -777,6 +796,8 @@ void sigkill_wait(pid_t pid) {
}
void sigkill_waitp(pid_t *pid) {
+ PROTECT_ERRNO;
+
if (!pid)
return;
if (*pid <= 1)
@@ -928,6 +949,17 @@ noreturn void freeze(void) {
sync();
+ /* Let's not freeze right away, but keep reaping zombies. */
+ for (;;) {
+ int r;
+ siginfo_t si = {};
+
+ r = waitid(P_ALL, 0, &si, WEXITED);
+ if (r < 0 && errno != EINTR)
+ break;
+ }
+
+ /* waitid() failed with an unexpected error, things are really borked. Freeze now! */
for (;;)
pause();
}
@@ -1075,7 +1107,7 @@ int ioprio_parse_priority(const char *s, int *ret) {
static pid_t cached_pid = CACHED_PID_UNSET;
-static void reset_cached_pid(void) {
+void reset_cached_pid(void) {
/* Invoked in the child after a fork(), i.e. at the first moment the PID changed */
cached_pid = CACHED_PID_UNSET;
}
@@ -1134,6 +1166,244 @@ int must_be_root(void) {
return -EPERM;
}
+int safe_fork_full(
+ const char *name,
+ const int except_fds[],
+ size_t n_except_fds,
+ ForkFlags flags,
+ pid_t *ret_pid) {
+
+ pid_t original_pid, pid;
+ sigset_t saved_ss, ss;
+ bool block_signals = false;
+ int prio, r;
+
+ /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
+ * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */
+
+ prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG;
+
+ original_pid = getpid_cached();
+
+ if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
+
+ /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
+ * be sure that SIGTERMs are not lost we might send to the child. */
+
+ if (sigfillset(&ss) < 0)
+ return log_full_errno(prio, errno, "Failed to reset signal set: %m");
+
+ block_signals = true;
+
+ } else if (flags & FORK_WAIT) {
+
+ /* Let's block SIGCHLD at least, so that we can safely watch for the child process */
+
+ if (sigemptyset(&ss) < 0)
+ return log_full_errno(prio, errno, "Failed to clear signal set: %m");
+
+ if (sigaddset(&ss, SIGCHLD) < 0)
+ return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m");
+
+ block_signals = true;
+ }
+
+ if (block_signals)
+ if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0)
+ return log_full_errno(prio, errno, "Failed to set signal mask: %m");
+
+ if (flags & FORK_NEW_MOUNTNS)
+ pid = raw_clone(SIGCHLD|CLONE_NEWNS);
+ else
+ pid = fork();
+ if (pid < 0) {
+ r = -errno;
+
+ if (block_signals) /* undo what we did above */
+ (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL);
+
+ return log_full_errno(prio, r, "Failed to fork: %m");
+ }
+ if (pid > 0) {
+ /* We are in the parent process */
+
+ log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
+
+ if (flags & FORK_WAIT) {
+ r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0));
+ if (r < 0)
+ return r;
+ if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
+ return -EPROTO;
+ }
+
+ if (block_signals) /* undo what we did above */
+ (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL);
+
+ if (ret_pid)
+ *ret_pid = pid;
+
+ return 1;
+ }
+
+ /* We are in the child process */
+
+ if (flags & FORK_REOPEN_LOG) {
+ /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */
+ log_close();
+ log_set_open_when_needed(true);
+ }
+
+ if (name) {
+ r = rename_process(name);
+ if (r < 0)
+ log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG,
+ r, "Failed to rename process, ignoring: %m");
+ }
+
+ if (flags & FORK_DEATHSIG)
+ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) {
+ log_full_errno(prio, errno, "Failed to set death signal: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (flags & FORK_RESET_SIGNALS) {
+ r = reset_all_signal_handlers();
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to reset signal handlers: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ /* This implicitly undoes the signal mask stuff we did before the fork()ing above */
+ r = reset_signal_mask();
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to reset signal mask: %m");
+ _exit(EXIT_FAILURE);
+ }
+ } else if (block_signals) { /* undo what we did above */
+ if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) {
+ log_full_errno(prio, errno, "Failed to restore signal mask: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ if (flags & FORK_DEATHSIG) {
+ pid_t ppid;
+ /* Let's see if the parent PID is still the one we started from? If not, then the parent
+ * already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */
+
+ ppid = getppid();
+ if (ppid == 0)
+ /* Parent is in a differn't PID namespace. */;
+ else if (ppid != original_pid) {
+ log_debug("Parent died early, raising SIGTERM.");
+ (void) raise(SIGTERM);
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ if (flags & FORK_CLOSE_ALL_FDS) {
+ /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */
+ log_close();
+
+ r = close_all_fds(except_fds, n_except_fds);
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to close all file descriptors: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ /* When we were asked to reopen the logs, do so again now */
+ if (flags & FORK_REOPEN_LOG) {
+ log_open();
+ log_set_open_when_needed(false);
+ }
+
+ if (flags & FORK_NULL_STDIO) {
+ r = make_null_stdio();
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ if (ret_pid)
+ *ret_pid = getpid_cached();
+
+ return 0;
+}
+
+int fork_agent(const char *name, const int except[], unsigned n_except, pid_t *ret_pid, const char *path, ...) {
+ bool stdout_is_tty, stderr_is_tty;
+ unsigned n, i;
+ va_list ap;
+ char **l;
+ int r;
+
+ assert(path);
+
+ /* Spawns a temporary TTY agent, making sure it goes away when we go away */
+
+ r = safe_fork_full(name, except, n_except, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, ret_pid);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0;
+
+ /* In the child: */
+
+ stdout_is_tty = isatty(STDOUT_FILENO);
+ stderr_is_tty = isatty(STDERR_FILENO);
+
+ if (!stdout_is_tty || !stderr_is_tty) {
+ int fd;
+
+ /* Detach from stdout/stderr. and reopen
+ * /dev/tty for them. This is important to
+ * ensure that when systemctl is started via
+ * popen() or a similar call that expects to
+ * read EOF we actually do generate EOF and
+ * not delay this indefinitely by because we
+ * keep an unused copy of stdin around. */
+ fd = open("/dev/tty", O_WRONLY);
+ if (fd < 0) {
+ log_error_errno(errno, "Failed to open /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
+ log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
+ log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (fd > STDERR_FILENO)
+ close(fd);
+ }
+
+ /* Count arguments */
+ va_start(ap, path);
+ for (n = 0; va_arg(ap, char*); n++)
+ ;
+ va_end(ap);
+
+ /* Allocate strv */
+ l = alloca(sizeof(char *) * (n + 1));
+
+ /* Fill in arguments */
+ va_start(ap, path);
+ for (i = 0; i <= n; i++)
+ l[i] = va_arg(ap, char*);
+ va_end(ap);
+
+ execv(path, l);
+ _exit(EXIT_FAILURE);
+}
+
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index 1b7e692060..9fabe4a5be 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -21,6 +21,7 @@
***/
#include <alloca.h>
+#include <errno.h>
#include <sched.h>
#include <signal.h>
#include <stdbool.h>
@@ -61,7 +62,16 @@ int get_process_environ(pid_t pid, char **environ);
int get_process_ppid(pid_t pid, pid_t *ppid);
int wait_for_terminate(pid_t pid, siginfo_t *status);
-int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
+
+typedef enum WaitFlags {
+ WAIT_LOG_ABNORMAL = 1U << 0,
+ WAIT_LOG_NON_ZERO_EXIT_STATUS = 1U << 1,
+
+ /* A shortcut for requesting the most complete logging */
+ WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS,
+} WaitFlags;
+
+int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags);
int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout);
void sigkill_wait(pid_t pid);
@@ -106,8 +116,13 @@ int sigchld_code_from_string(const char *s) _pure_;
int sched_policy_to_string_alloc(int i, char **s);
int sched_policy_from_string(const char *s);
-#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p))
-#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
+static inline pid_t PTR_TO_PID(const void *p) {
+ return (pid_t) ((uintptr_t) p);
+}
+
+static inline void* PID_TO_PTR(pid_t pid) {
+ return (void*) ((uintptr_t) pid);
+}
void valgrind_summary_hack(void);
@@ -137,8 +152,54 @@ static inline bool pid_is_valid(pid_t p) {
return p > 0;
}
+static inline int sched_policy_to_string_alloc_with_check(int n, char **s) {
+ if (!sched_policy_is_valid(n))
+ return -EINVAL;
+
+ return sched_policy_to_string_alloc(n, s);
+}
+
int ioprio_parse_priority(const char *s, int *ret);
pid_t getpid_cached(void);
+void reset_cached_pid(void);
int must_be_root(void);
+
+typedef enum ForkFlags {
+ FORK_RESET_SIGNALS = 1U << 0,
+ FORK_CLOSE_ALL_FDS = 1U << 1,
+ FORK_DEATHSIG = 1U << 2,
+ FORK_NULL_STDIO = 1U << 3,
+ FORK_REOPEN_LOG = 1U << 4,
+ FORK_LOG = 1U << 5,
+ FORK_WAIT = 1U << 6,
+ FORK_NEW_MOUNTNS = 1U << 7,
+} ForkFlags;
+
+int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
+
+static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
+ return safe_fork_full(name, NULL, 0, flags, ret_pid);
+}
+
+int fork_agent(const char *name, const int except[], unsigned n_except, pid_t *pid, const char *path, ...);
+
+#if SIZEOF_PID_T == 4
+/* The highest possibly (theoretic) pid_t value on this architecture. */
+#define PID_T_MAX ((pid_t) INT32_MAX)
+/* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value
+ * the kernel will potentially assign. This reflects a value compiled into the kernel (PID_MAX_LIMIT), and sets the
+ * upper boundary on what may be written to the /proc/sys/kernel/pid_max sysctl (but do note that the sysctl is off by
+ * 1, since PID 0 can never exist and there can hence only be one process less than the limit would suggest). Since
+ * these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at
+ * least to define them here too. */
+#define TASKS_MAX 4194303U
+#elif SIZEOF_PID_T == 2
+#define PID_T_MAX ((pid_t) INT16_MAX)
+#define TASKS_MAX 32767U
+#else
+#error "Unknown pid_t size"
+#endif
+
+assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX)
diff --git a/src/basic/procfs-util.c b/src/basic/procfs-util.c
new file mode 100644
index 0000000000..9bb42cc7ba
--- /dev/null
+++ b/src/basic/procfs-util.c
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+
+#include "alloc-util.h"
+#include "fileio.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "procfs-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+
+int procfs_tasks_get_limit(uint64_t *ret) {
+ _cleanup_free_ char *value = NULL;
+ uint64_t pid_max, threads_max;
+ int r;
+
+ assert(ret);
+
+ /* So there are two sysctl files that control the system limit of processes:
+ *
+ * 1. kernel.threads-max: this is probably the sysctl that makes more sense, as it directly puts a limit on
+ * concurrent tasks.
+ *
+ * 2. kernel.pid_max: this limits the numeric range PIDs can take, and thus indirectly also limits the number
+ * of concurrent threads. AFAICS it's primarily a compatibility concept: some crappy old code used a signed
+ * 16bit type for PIDs, hence the kernel provides a way to ensure the PIDs never go beyond INT16_MAX by
+ * default.
+ *
+ * By default #2 is set to much lower values than #1, hence the limit people come into contact with first, as
+ * it's the lowest boundary they need to bump when they want higher number of processes.
+ *
+ * Also note the weird definition of #2: PIDs assigned will be kept below this value, which means the number of
+ * tasks that can be created is one lower, as PID 0 is not a valid process ID. */
+
+ r = read_one_line_file("/proc/sys/kernel/pid_max", &value);
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(value, &pid_max);
+ if (r < 0)
+ return r;
+
+ value = mfree(value);
+ r = read_one_line_file("/proc/sys/kernel/threads-max", &value);
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(value, &threads_max);
+ if (r < 0)
+ return r;
+
+ /* Subtract one from pid_max, since PID 0 is not a valid PID */
+ *ret = MIN(pid_max-1, threads_max);
+ return 0;
+}
+
+int procfs_tasks_set_limit(uint64_t limit) {
+ char buffer[DECIMAL_STR_MAX(uint64_t)+1];
+ _cleanup_free_ char *value = NULL;
+ uint64_t pid_max;
+ int r;
+
+ if (limit == 0) /* This makes no sense, we are userspace and hence count as tasks too, and we want to live,
+ * hence the limit conceptually has to be above 0. Also, most likely if anyone asks for a zero
+ * limit he/she probably means "no limit", hence let's better refuse this to avoid
+ * confusion. */
+ return -EINVAL;
+
+ /* The Linux kernel doesn't allow this value to go below 20, hence don't allow this either, higher values than
+ * TASKS_MAX are not accepted by the pid_max sysctl. We'll treat anything this high as "unbounded" and hence
+ * set it to the maximum. */
+ limit = CLAMP(limit, 20U, TASKS_MAX);
+
+ r = read_one_line_file("/proc/sys/kernel/pid_max", &value);
+ if (r < 0)
+ return r;
+ r = safe_atou64(value, &pid_max);
+ if (r < 0)
+ return r;
+
+ /* As pid_max is about the numeric pid_t range we'll bump it if necessary, but only ever increase it, never
+ * decrease it, as threads-max is the much more relevant sysctl. */
+ if (limit > pid_max-1) {
+ sprintf(buffer, "%" PRIu64, limit+1); /* Add one, since PID 0 is not a valid PID */
+ r = write_string_file("/proc/sys/kernel/pid_max", buffer, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0)
+ return r;
+ }
+
+ sprintf(buffer, "%" PRIu64, limit);
+ r = write_string_file("/proc/sys/kernel/threads-max", buffer, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0) {
+ uint64_t threads_max;
+
+ /* Hmm, we couldn't write this? If so, maybe it was already set properly? In that case let's not
+ * generate an error */
+
+ value = mfree(value);
+ if (read_one_line_file("/proc/sys/kernel/threads-max", &value) < 0)
+ return r; /* return original error */
+
+ if (safe_atou64(value, &threads_max) < 0)
+ return r; /* return original error */
+
+ if (MIN(pid_max-1, threads_max) != limit)
+ return r; /* return original error */
+
+ /* Yay! Value set already matches what we were trying to set, hence consider this a success. */
+ }
+
+ return 0;
+}
+
+int procfs_tasks_get_current(uint64_t *ret) {
+ _cleanup_free_ char *value = NULL;
+ const char *p, *nr;
+ size_t n;
+ int r;
+
+ assert(ret);
+
+ r = read_one_line_file("/proc/loadavg", &value);
+ if (r < 0)
+ return r;
+
+ /* Look for the second part of the fourth field, which is separated by a slash from the first part. None of the
+ * earlier fields use a slash, hence let's use this to find the right spot. */
+ p = strchr(value, '/');
+ if (!p)
+ return -EINVAL;
+
+ p++;
+ n = strspn(p, DIGITS);
+ nr = strndupa(p, n);
+
+ return safe_atou64(nr, ret);
+}
diff --git a/src/basic/procfs-util.h b/src/basic/procfs-util.h
new file mode 100644
index 0000000000..7466acd7f3
--- /dev/null
+++ b/src/basic/procfs-util.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <inttypes.h>
+
+int procfs_tasks_get_limit(uint64_t *ret);
+int procfs_tasks_set_limit(uint64_t limit);
+int procfs_tasks_get_current(uint64_t *ret);
diff --git a/src/basic/random-util.c b/src/basic/random-util.c
index 1bc8000896..7457815fa2 100644
--- a/src/basic/random-util.c
+++ b/src/basic/random-util.c
@@ -21,11 +21,12 @@
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
+#include <linux/random.h>
#include <stdbool.h>
+#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/time.h>
-#include <linux/random.h>
-#include <stdint.h>
#if HAVE_SYS_AUXV_H
# include <sys/auxv.h>
diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h
index f01b73a8fe..8c95380305 100644
--- a/src/basic/raw-clone.h
+++ b/src/basic/raw-clone.h
@@ -30,28 +30,27 @@
* raw_clone() - uses clone to create a new process with clone flags
* @flags: Flags to pass to the clone system call
*
- * Uses the clone system call to create a new process with the cloning
- * flags and termination signal passed in the flags parameter. Opposed
- * to glibc's clone funtion, using this function does not set up a
- * separate stack for the child, but relies on copy-on-write semantics
- * on the one stack at a common virtual address, just as fork does.
+ * Uses the clone system call to create a new process with the cloning flags and termination signal passed in the flags
+ * parameter. Opposed to glibc's clone funtion, using this function does not set up a separate stack for the child, but
+ * relies on copy-on-write semantics on the one stack at a common virtual address, just as fork does.
*
- * To obtain copy-on-write semantics, flags must not contain CLONE_VM,
- * and thus CLONE_THREAD and CLONE_SIGHAND (which require CLONE_VM) are
- * not usabale.
- * Additionally, as this function does not pass the ptid, newtls and ctid
- * parameters to the kernel, flags must not contain CLONE_PARENT_SETTID,
- * CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS.
+ * To obtain copy-on-write semantics, flags must not contain CLONE_VM, and thus CLONE_THREAD and CLONE_SIGHAND (which
+ * require CLONE_VM) are not usable.
+ *
+ * Additionally, as this function does not pass the ptid, newtls and ctid parameters to the kernel, flags must not
+ * contain CLONE_PARENT_SETTID, CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS.
*
* Returns: 0 in the child process and the child process id in the parent.
*/
-static inline int raw_clone(unsigned long flags) {
+static inline pid_t raw_clone(unsigned long flags) {
+ pid_t ret;
+
assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID|
CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0);
#if defined(__s390x__) || defined(__s390__) || defined(__CRIS__)
/* On s390/s390x and cris the order of the first and second arguments
* of the raw clone() system call is reversed. */
- return (int) syscall(__NR_clone, NULL, flags);
+ ret = (pid_t) syscall(__NR_clone, NULL, flags);
#elif defined(__sparc__) && defined(__arch64__)
{
/**
@@ -60,8 +59,8 @@ static inline int raw_clone(unsigned long flags) {
* %o1. Inline assembly is needed to get the flag returned
* in %o1.
*/
- int in_child;
- int child_pid;
+ int in_child, child_pid;
+
asm volatile("mov %2, %%g1\n\t"
"mov %3, %%o0\n\t"
"mov 0 , %%o1\n\t"
@@ -71,12 +70,15 @@ static inline int raw_clone(unsigned long flags) {
"=r"(in_child), "=r"(child_pid) :
"i"(__NR_clone), "r"(flags) :
"%o1", "%o0", "%g1" );
- if (in_child)
- return 0;
- else
- return child_pid;
+
+ ret = in_child ? 0 : child_pid;
}
#else
- return (int) syscall(__NR_clone, flags, NULL);
+ ret = (pid_t) syscall(__NR_clone, flags, NULL);
#endif
+
+ if (ret == 0)
+ reset_cached_pid();
+
+ return ret;
}
diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h
index 1127e326b2..ad63e9be40 100644
--- a/src/basic/rm-rf.h
+++ b/src/basic/rm-rf.h
@@ -22,6 +22,8 @@
#include <sys/stat.h>
+#include "util.h"
+
typedef enum RemoveFlags {
REMOVE_ONLY_DIRECTORIES = 1,
REMOVE_ROOT = 2,
@@ -34,6 +36,7 @@ int rm_rf(const char *path, RemoveFlags flags);
/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
static inline void rm_rf_physical_and_free(char *p) {
+ PROTECT_ERRNO;
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
free(p);
}
diff --git a/src/basic/securebits-util.c b/src/basic/securebits-util.c
index b5f6418a6c..441d386f9e 100644
--- a/src/basic/securebits-util.c
+++ b/src/basic/securebits-util.c
@@ -19,6 +19,7 @@
***/
#include <errno.h>
+#include <stdio.h>
#include "alloc-util.h"
#include "extract-word.h"
diff --git a/src/basic/securebits-util.h b/src/basic/securebits-util.h
index aaa192f0a5..069d215488 100644
--- a/src/basic/securebits-util.h
+++ b/src/basic/securebits-util.h
@@ -24,6 +24,14 @@
int secure_bits_to_string_alloc(int i, char **s);
int secure_bits_from_string(const char *s);
+
static inline bool secure_bits_is_valid(int i) {
return ((SECURE_ALL_BITS | SECURE_ALL_LOCKS) & i) == i;
}
+
+static inline int secure_bits_to_string_alloc_with_check(int n, char **s) {
+ if (!secure_bits_is_valid(n))
+ return -EINVAL;
+
+ return secure_bits_to_string_alloc(n, s);
+}
diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h
index 76b239b1fc..f6c3396ebe 100644
--- a/src/basic/signal-util.h
+++ b/src/basic/signal-util.h
@@ -55,3 +55,10 @@ static inline void block_signals_reset(sigset_t *ss) {
static inline bool SIGNAL_VALID(int signo) {
return signo > 0 && signo < _NSIG;
}
+
+static inline const char* signal_to_string_with_check(int n) {
+ if (!SIGNAL_VALID(n))
+ return NULL;
+
+ return signal_to_string(n);
+}
diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c
index e0f1c9f1c7..f0018f013f 100644
--- a/src/basic/smack-util.c
+++ b/src/basic/smack-util.c
@@ -136,7 +136,7 @@ int mac_smack_apply_pid(pid_t pid, const char *label) {
int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
struct stat st;
- int r = 0;
+ int r;
assert(path);
diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c
index 20be406371..97f3ebe2af 100644
--- a/src/basic/socket-label.c
+++ b/src/basic/socket-label.c
@@ -29,6 +29,7 @@
#include "alloc-util.h"
#include "fd-util.h"
+#include "fs-util.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
@@ -51,6 +52,7 @@ int socket_address_listen(
const char *label) {
_cleanup_close_ int fd = -1;
+ const char *p;
int r, one;
assert(a);
@@ -112,19 +114,23 @@ int socket_address_listen(
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
return -errno;
- if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
+ p = socket_address_get_path(a);
+ if (p) {
/* Create parents */
- (void) mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode);
+ (void) mkdir_parents_label(p, directory_mode);
/* Enforce the right access mode for the socket */
RUN_WITH_UMASK(~socket_mode) {
r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
if (r == -EADDRINUSE) {
/* Unlink and try again */
- unlink(a->sockaddr.un.sun_path);
- if (bind(fd, &a->sockaddr.sa, a->size) < 0)
- return -errno;
- } else if (r < 0)
+
+ if (unlink(p) < 0)
+ return r; /* didn't work, return original error */
+
+ r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
+ }
+ if (r < 0)
return r;
}
} else {
@@ -136,6 +142,11 @@ int socket_address_listen(
if (listen(fd, backlog) < 0)
return -errno;
+ /* Let's trigger an inotify event on the socket node, so that anyone waiting for this socket to be connectable
+ * gets notified */
+ if (p)
+ (void) touch(p);
+
r = fd;
fd = -1;
diff --git a/src/basic/socket-protocol-list.c b/src/basic/socket-protocol-list.c
new file mode 100644
index 0000000000..9ab93d1c7e
--- /dev/null
+++ b/src/basic/socket-protocol-list.c
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/in.h>
+#include <string.h>
+
+#include "socket-protocol-list.h"
+#include "macro.h"
+
+static const struct socket_protocol_name* lookup_socket_protocol(register const char *str, register GPERF_LEN_TYPE len);
+
+#include "socket-protocol-from-name.h"
+#include "socket-protocol-to-name.h"
+
+const char *socket_protocol_to_name(int id) {
+
+ if (id < 0)
+ return NULL;
+
+ if (id >= (int) ELEMENTSOF(socket_protocol_names))
+ return NULL;
+
+ return socket_protocol_names[id];
+}
+
+int socket_protocol_from_name(const char *name) {
+ const struct socket_protocol_name *sc;
+
+ assert(name);
+
+ sc = lookup_socket_protocol(name, strlen(name));
+ if (!sc)
+ return 0;
+
+ return sc->id;
+}
+
+int socket_protocol_max(void) {
+ return ELEMENTSOF(socket_protocol_names);
+}
diff --git a/src/basic/socket-protocol-list.h b/src/basic/socket-protocol-list.h
new file mode 100644
index 0000000000..12fd053382
--- /dev/null
+++ b/src/basic/socket-protocol-list.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#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 <http://www.gnu.org/licenses/>.
+***/
+
+const char *socket_protocol_to_name(int id);
+int socket_protocol_from_name(const char *name);
+
+int socket_protocol_max(void);
diff --git a/src/basic/socket-protocol-to-name.awk b/src/basic/socket-protocol-to-name.awk
new file mode 100644
index 0000000000..4848a7631a
--- /dev/null
+++ b/src/basic/socket-protocol-to-name.awk
@@ -0,0 +1,9 @@
+BEGIN{
+ print "static const char* const socket_protocol_names[] = { "
+}
+!/HOPOPTS/ {
+ printf " [IPPROTO_%s] = \"%s\",\n", $1, tolower($1)
+}
+END{
+ print "};"
+}
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index a458fc2902..2c70cade14 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -41,6 +41,7 @@
#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
@@ -55,9 +56,19 @@
# define IDN_FLAGS 0
#endif
+static const char* const socket_address_type_table[] = {
+ [SOCK_STREAM] = "Stream",
+ [SOCK_DGRAM] = "Datagram",
+ [SOCK_RAW] = "Raw",
+ [SOCK_RDM] = "ReliableDatagram",
+ [SOCK_SEQPACKET] = "SequentialPacket",
+ [SOCK_DCCP] = "DatagramCongestionControl",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int);
+
int socket_address_parse(SocketAddress *a, const char *s) {
char *e, *n;
- unsigned u;
int r;
assert(a);
@@ -67,6 +78,8 @@ int socket_address_parse(SocketAddress *a, const char *s) {
a->type = SOCK_STREAM;
if (*s == '[') {
+ uint16_t port;
+
/* IPv6 in [x:.....:z]:p notation */
e = strchr(s+1, ']');
@@ -84,15 +97,12 @@ int socket_address_parse(SocketAddress *a, const char *s) {
return -EINVAL;
e++;
- r = safe_atou(e, &u);
+ r = parse_ip_port(e, &port);
if (r < 0)
return r;
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
+ a->sockaddr.in6.sin6_port = htobe16(port);
a->size = sizeof(struct sockaddr_in6);
} else if (*s == '/') {
@@ -123,12 +133,13 @@ int socket_address_parse(SocketAddress *a, const char *s) {
} else if (startswith(s, "vsock:")) {
/* AF_VSOCK socket in vsock:cid:port notation */
const char *cid_start = s + STRLEN("vsock:");
+ unsigned port;
e = strchr(cid_start, ':');
if (!e)
return -EINVAL;
- r = safe_atou(e+1, &u);
+ r = safe_atou(e+1, &port);
if (r < 0)
return r;
@@ -141,19 +152,18 @@ int socket_address_parse(SocketAddress *a, const char *s) {
a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
a->sockaddr.vm.svm_family = AF_VSOCK;
- a->sockaddr.vm.svm_port = u;
+ a->sockaddr.vm.svm_port = port;
a->size = sizeof(struct sockaddr_vm);
} else {
+ uint16_t port;
+
e = strchr(s, ':');
if (e) {
- r = safe_atou(e+1, &u);
+ r = parse_ip_port(e + 1, &port);
if (r < 0)
return r;
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
n = strndupa(s, e-s);
/* IPv4 in w.x.y.z:p notation? */
@@ -164,7 +174,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
if (r > 0) {
/* Gotcha, it's a traditional IPv4 address */
a->sockaddr.in.sin_family = AF_INET;
- a->sockaddr.in.sin_port = htobe16((uint16_t)u);
+ a->sockaddr.in.sin_port = htobe16(port);
a->size = sizeof(struct sockaddr_in);
} else {
unsigned idx;
@@ -178,7 +188,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
return -EINVAL;
a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
+ a->sockaddr.in6.sin6_port = htobe16(port);
a->sockaddr.in6.sin6_scope_id = idx;
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
@@ -186,21 +196,18 @@ int socket_address_parse(SocketAddress *a, const char *s) {
} else {
/* Just a port */
- r = safe_atou(s, &u);
+ r = parse_ip_port(s, &port);
if (r < 0)
return r;
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
if (socket_ipv6_is_supported()) {
a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
+ a->sockaddr.in6.sin6_port = htobe16(port);
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
} else {
a->sockaddr.in.sin_family = AF_INET;
- a->sockaddr.in.sin_port = htobe16((uint16_t)u);
+ a->sockaddr.in.sin_port = htobe16(port);
a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
a->size = sizeof(struct sockaddr_in);
}
@@ -528,22 +535,25 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) {
return socket_address_equal(a, &b);
}
-int sockaddr_port(const struct sockaddr *_sa, unsigned *port) {
+int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) {
union sockaddr_union *sa = (union sockaddr_union*) _sa;
+ /* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */
+
assert(sa);
switch (sa->sa.sa_family) {
+
case AF_INET:
- *port = be16toh(sa->in.sin_port);
+ *ret_port = be16toh(sa->in.sin_port);
return 0;
case AF_INET6:
- *port = be16toh(sa->in6.sin6_port);
+ *ret_port = be16toh(sa->in6.sin6_port);
return 0;
case AF_VSOCK:
- *port = sa->vm.svm_port;
+ *ret_port = sa->vm.svm_port;
return 0;
default:
@@ -807,6 +817,18 @@ static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIN
DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
+SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *n) {
+ int r;
+
+ r = parse_boolean(n);
+ if (r > 0)
+ return SOCKET_ADDRESS_IPV6_ONLY;
+ if (r == 0)
+ return SOCKET_ADDRESS_BOTH;
+
+ return socket_address_bind_ipv6_only_from_string(n);
+}
+
bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) {
assert(a);
assert(b);
@@ -941,58 +963,80 @@ int getpeercred(int fd, struct ucred *ucred) {
if (n != sizeof(struct ucred))
return -EIO;
- /* Check if the data is actually useful and not suppressed due
- * to namespacing issues */
- if (u.pid <= 0)
- return -ENODATA;
- if (u.uid == UID_INVALID)
- return -ENODATA;
- if (u.gid == GID_INVALID)
+ /* Check if the data is actually useful and not suppressed due to namespacing issues */
+ if (!pid_is_valid(u.pid))
return -ENODATA;
+ /* Note that we don't check UID/GID here, as namespace translation works differently there: instead of
+ * receiving in "invalid" user/group we get the overflow UID/GID. */
+
*ucred = u;
return 0;
}
int getpeersec(int fd, char **ret) {
+ _cleanup_free_ char *s = NULL;
socklen_t n = 64;
- char *s;
- int r;
assert(fd >= 0);
assert(ret);
- s = new0(char, n);
- if (!s)
- return -ENOMEM;
+ for (;;) {
+ s = new0(char, n+1);
+ if (!s)
+ return -ENOMEM;
- r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
- if (r < 0) {
- free(s);
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0)
+ break;
if (errno != ERANGE)
return -errno;
- s = new0(char, n);
- if (!s)
- return -ENOMEM;
-
- r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
- if (r < 0) {
- free(s);
- return -errno;
- }
+ s = mfree(s);
}
- if (isempty(s)) {
- free(s);
+ if (isempty(s))
return -EOPNOTSUPP;
- }
*ret = s;
+ s = NULL;
+
return 0;
}
+int getpeergroups(int fd, gid_t **ret) {
+ socklen_t n = sizeof(gid_t) * 64;
+ _cleanup_free_ gid_t *d = NULL;
+
+ assert(fd);
+ assert(ret);
+
+ for (;;) {
+ d = malloc(n);
+ if (!d)
+ return -ENOMEM;
+
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0)
+ break;
+
+ if (errno != ERANGE)
+ return -errno;
+
+ d = mfree(d);
+ }
+
+ assert_se(n % sizeof(gid_t) == 0);
+ n /= sizeof(gid_t);
+
+ if ((socklen_t) (int) n != n)
+ return -E2BIG;
+
+ *ret = d;
+ d = NULL;
+
+ return (int) n;
+}
+
int send_one_fd_sa(
int transport_fd,
int fd,
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 272e74b0cc..49c937aef5 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -36,16 +36,26 @@
#include "util.h"
union sockaddr_union {
+ /* The minimal, abstract version */
struct sockaddr sa;
+
+ /* The libc provided version that allocates "enough room" for every protocol */
+ struct sockaddr_storage storage;
+
+ /* Protoctol-specific implementations */
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_un un;
struct sockaddr_nl nl;
- struct sockaddr_storage storage;
struct sockaddr_ll ll;
struct sockaddr_vm vm;
+
/* Ensure there is enough space to store Infiniband addresses */
uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)];
+
+ /* Ensure there is enough space after the AF_UNIX sun_path for one more NUL byte, just to be sure that the path
+ * component is always followed by at least one NUL byte. */
+ uint8_t un_buffer[sizeof(struct sockaddr_un) + 1];
};
typedef struct SocketAddress {
@@ -72,6 +82,9 @@ typedef enum SocketAddressBindIPv6Only {
#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
+const char* socket_address_type_to_string(int t) _const_;
+int socket_address_type_from_string(const char *s) _pure_;
+
int socket_address_parse(SocketAddress *a, const char *s);
int socket_address_parse_and_warn(SocketAddress *a, const char *s);
int socket_address_parse_netlink(SocketAddress *a, const char *s);
@@ -117,6 +130,7 @@ int getnameinfo_pretty(int fd, char **ret);
const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
+SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *s);
int netlink_family_to_string_alloc(int b, char **s);
int netlink_family_from_string(const char *s) _pure_;
@@ -134,6 +148,7 @@ bool address_label_valid(const char *p);
int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
+int getpeergroups(int fd, gid_t **ret);
int send_one_fd_sa(int transport_fd,
int fd,
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
index 96fc8b3787..3a54103f1b 100644
--- a/src/basic/stat-util.c
+++ b/src/basic/stat-util.c
@@ -21,10 +21,11 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#include <linux/magic.h>
+#include <sched.h>
+#include <sys/stat.h>
#include <sys/statvfs.h>
+#include <sys/types.h>
#include <unistd.h>
#include "dirent-util.h"
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index 7e2f596edc..9f2c01d864 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -30,6 +30,7 @@
#include "gunicode.h"
#include "macro.h"
#include "string-util.h"
+#include "terminal-util.h"
#include "utf8.h"
#include "util.h"
@@ -648,7 +649,17 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
return ret;
}
-char *strip_tab_ansi(char **ibuf, size_t *_isz) {
+static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) {
+ if (!offsets)
+ return;
+
+ if ((size_t) diff < offsets[0])
+ shift[0] += size;
+ if ((size_t) diff < offsets[1])
+ shift[1] += size;
+}
+
+char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
const char *i, *begin = NULL;
enum {
STATE_OTHER,
@@ -656,7 +667,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
STATE_BRACKET
} state = STATE_OTHER;
char *obuf = NULL;
- size_t osz = 0, isz;
+ size_t osz = 0, isz, shift[2] = {};
FILE *f;
assert(ibuf);
@@ -684,15 +695,18 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
break;
else if (*i == '\x1B')
state = STATE_ESCAPE;
- else if (*i == '\t')
+ else if (*i == '\t') {
fputs(" ", f);
- else
+ advance_offsets(i - *ibuf, highlight, shift, 7);
+ } else
fputc(*i, f);
+
break;
case STATE_ESCAPE:
if (i >= *ibuf + isz) { /* EOT */
fputc('\x1B', f);
+ advance_offsets(i - *ibuf, highlight, shift, 1);
break;
} else if (*i == '[') {
state = STATE_BRACKET;
@@ -700,6 +714,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
} else {
fputc('\x1B', f);
fputc(*i, f);
+ advance_offsets(i - *ibuf, highlight, shift, 1);
state = STATE_OTHER;
}
@@ -711,6 +726,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
(!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) {
fputc('\x1B', f);
fputc('[', f);
+ advance_offsets(i - *ibuf, highlight, shift, 2);
state = STATE_OTHER;
i = begin-1;
} else if (*i == 'm')
@@ -732,6 +748,11 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
if (_isz)
*_isz = osz;
+ if (highlight) {
+ highlight[0] += shift[0];
+ highlight[1] += shift[1];
+ }
+
return obuf;
}
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
index 09a737ad37..08eda4fce0 100644
--- a/src/basic/string-util.h
+++ b/src/basic/string-util.h
@@ -52,15 +52,15 @@ static inline bool streq_ptr(const char *a, const char *b) {
}
static inline const char* strempty(const char *s) {
- return s ? s : "";
+ return s ?: "";
}
static inline const char* strnull(const char *s) {
- return s ? s : "(null)";
+ return s ?: "(null)";
}
static inline const char *strna(const char *s) {
- return s ? s : "n/a";
+ return s ?: "n/a";
}
static inline bool isempty(const char *p) {
@@ -177,7 +177,7 @@ char* strshorten(char *s, size_t l);
char *strreplace(const char *text, const char *old_string, const char *new_string);
-char *strip_tab_ansi(char **p, size_t *l);
+char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]);
char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_;
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index 48ee799ad4..42336e8fdf 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -875,31 +875,30 @@ bool on_tty(void) {
}
int make_stdio(int fd) {
- int r, s, t;
+ int r = 0;
assert(fd >= 0);
- r = dup2(fd, STDIN_FILENO);
- s = dup2(fd, STDOUT_FILENO);
- t = dup2(fd, STDERR_FILENO);
+ if (dup2(fd, STDIN_FILENO) < 0 && r >= 0)
+ r = -errno;
+ if (dup2(fd, STDOUT_FILENO) < 0 && r >= 0)
+ r = -errno;
+ if (dup2(fd, STDERR_FILENO) < 0 && r >= 0)
+ r = -errno;
if (fd >= 3)
safe_close(fd);
- if (r < 0 || s < 0 || t < 0)
- return -errno;
-
- /* Explicitly unset O_CLOEXEC, since if fd was < 3, then
- * dup2() was a NOP and the bit hence possibly set. */
+ /* Explicitly unset O_CLOEXEC, since if fd was < 3, then dup2() was a NOP and the bit hence possibly set. */
stdio_unset_cloexec();
- return 0;
+ return r;
}
int make_null_stdio(void) {
int null_fd;
- null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
+ null_fd = open("/dev/null", O_RDWR|O_NOCTTY|O_CLOEXEC);
if (null_fd < 0)
return -errno;
@@ -1094,7 +1093,6 @@ int ptsname_namespace(int pty, char **ret) {
int openpt_in_namespace(pid_t pid, int flags) {
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
- siginfo_t si;
pid_t child;
int r;
@@ -1107,11 +1105,10 @@ int openpt_in_namespace(pid_t pid, int flags) {
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- child = fork();
- if (child < 0)
- return -errno;
-
- if (child == 0) {
+ r = safe_fork("(sd-openpt)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ if (r < 0)
+ return r;
+ if (r == 0) {
int master;
pair[0] = safe_close(pair[0]);
@@ -1135,10 +1132,10 @@ int openpt_in_namespace(pid_t pid, int flags) {
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate(child, &si);
+ r = wait_for_terminate_and_check("(sd-openpt)", child, 0);
if (r < 0)
return r;
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ if (r != EXIT_SUCCESS)
return -EIO;
return receive_one_fd(pair[0], 0);
@@ -1147,7 +1144,6 @@ int openpt_in_namespace(pid_t pid, int flags) {
int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
- siginfo_t si;
pid_t child;
int r;
@@ -1158,11 +1154,10 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- child = fork();
- if (child < 0)
- return -errno;
-
- if (child == 0) {
+ r = safe_fork("(sd-terminal)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ if (r < 0)
+ return r;
+ if (r == 0) {
int master;
pair[0] = safe_close(pair[0]);
@@ -1183,10 +1178,10 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate(child, &si);
+ r = wait_for_terminate_and_check("(sd-terminal)", child, 0);
if (r < 0)
return r;
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ if (r != EXIT_SUCCESS)
return -EIO;
return receive_one_fd(pair[0], 0);
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index d56576ddbe..4a341e208f 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -38,6 +38,7 @@
#include "macro.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@@ -887,7 +888,6 @@ int parse_timestamp(const char *t, usec_t *usec) {
char *last_space, *tz = NULL;
ParseTimestampResult *shared, tmp;
int r;
- pid_t pid;
last_space = strrchr(t, ' ');
if (last_space != NULL && timezone_is_valid(last_space + 1))
@@ -900,15 +900,12 @@ int parse_timestamp(const char *t, usec_t *usec) {
if (shared == MAP_FAILED)
return negative_errno();
- pid = fork();
-
- if (pid == -1) {
- int fork_errno = errno;
+ r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
+ if (r < 0) {
(void) munmap(shared, sizeof *shared);
- return -fork_errno;
+ return r;
}
-
- if (pid == 0) {
+ if (r == 0) {
bool with_tz = true;
if (setenv("TZ", tz, 1) != 0) {
@@ -931,12 +928,6 @@ int parse_timestamp(const char *t, usec_t *usec) {
_exit(EXIT_SUCCESS);
}
- r = wait_for_terminate(pid, NULL);
- if (r < 0) {
- (void) munmap(shared, sizeof *shared);
- return r;
- }
-
tmp = *shared;
if (munmap(shared, sizeof *shared) != 0)
return negative_errno();
diff --git a/src/basic/unaligned.h b/src/basic/unaligned.h
index 201b3d227e..73302b4239 100644
--- a/src/basic/unaligned.h
+++ b/src/basic/unaligned.h
@@ -26,89 +26,77 @@
/* BE */
static inline uint16_t unaligned_read_be16(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
- return (((uint16_t) u[0]) << 8) |
- ((uint16_t) u[1]);
+ return be16toh(u->x);
}
static inline uint32_t unaligned_read_be32(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
- return (((uint32_t) unaligned_read_be16(u)) << 16) |
- ((uint32_t) unaligned_read_be16(u + 2));
+ return be32toh(u->x);
}
static inline uint64_t unaligned_read_be64(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
- return (((uint64_t) unaligned_read_be32(u)) << 32) |
- ((uint64_t) unaligned_read_be32(u + 4));
+ return be64toh(u->x);
}
static inline void unaligned_write_be16(void *_u, uint16_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
- u[0] = (uint8_t) (a >> 8);
- u[1] = (uint8_t) a;
+ u->x = be16toh(a);
}
static inline void unaligned_write_be32(void *_u, uint32_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
- unaligned_write_be16(u, (uint16_t) (a >> 16));
- unaligned_write_be16(u + 2, (uint16_t) a);
+ u->x = be32toh(a);
}
static inline void unaligned_write_be64(void *_u, uint64_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
- unaligned_write_be32(u, (uint32_t) (a >> 32));
- unaligned_write_be32(u + 4, (uint32_t) a);
+ u->x = be64toh(a);
}
/* LE */
static inline uint16_t unaligned_read_le16(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
- return (((uint16_t) u[1]) << 8) |
- ((uint16_t) u[0]);
+ return le16toh(u->x);
}
static inline uint32_t unaligned_read_le32(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
- return (((uint32_t) unaligned_read_le16(u + 2)) << 16) |
- ((uint32_t) unaligned_read_le16(u));
+ return le32toh(u->x);
}
static inline uint64_t unaligned_read_le64(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
- return (((uint64_t) unaligned_read_le32(u + 4)) << 32) |
- ((uint64_t) unaligned_read_le32(u));
+ return le64toh(u->x);
}
static inline void unaligned_write_le16(void *_u, uint16_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
- u[0] = (uint8_t) a;
- u[1] = (uint8_t) (a >> 8);
+ u->x = le16toh(a);
}
static inline void unaligned_write_le32(void *_u, uint32_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
- unaligned_write_le16(u, (uint16_t) a);
- unaligned_write_le16(u + 2, (uint16_t) (a >> 16));
+ u->x = le32toh(a);
}
static inline void unaligned_write_le64(void *_u, uint64_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
- unaligned_write_le32(u, (uint32_t) a);
- unaligned_write_le32(u + 4, (uint32_t) (a >> 32));
+ u->x = le64toh(a);
}
#if __BYTE_ORDER == __BIG_ENDIAN
diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c
index 403f288b57..0fa0472ee1 100644
--- a/src/basic/unit-name.c
+++ b/src/basic/unit-name.c
@@ -28,6 +28,7 @@
#include "glob-util.h"
#include "hexdecoct.h"
#include "path-util.h"
+#include "special.h"
#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
@@ -673,7 +674,7 @@ int slice_build_parent_slice(const char *slice, char **ret) {
if (!slice_name_is_valid(slice))
return -EINVAL;
- if (streq(slice, "-.slice")) {
+ if (streq(slice, SPECIAL_ROOT_SLICE)) {
*ret = NULL;
return 0;
}
@@ -686,7 +687,7 @@ int slice_build_parent_slice(const char *slice, char **ret) {
if (dash)
strcpy(dash, ".slice");
else {
- r = free_and_strdup(&s, "-.slice");
+ r = free_and_strdup(&s, SPECIAL_ROOT_SLICE);
if (r < 0) {
free(s);
return r;
@@ -710,7 +711,7 @@ int slice_build_subslice(const char *slice, const char*name, char **ret) {
if (!unit_prefix_is_valid(name))
return -EINVAL;
- if (streq(slice, "-.slice"))
+ if (streq(slice, SPECIAL_ROOT_SLICE))
subslice = strappend(name, ".slice");
else {
char *e;
@@ -735,7 +736,7 @@ bool slice_name_is_valid(const char *name) {
if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
return false;
- if (streq(name, "-.slice"))
+ if (streq(name, SPECIAL_ROOT_SLICE))
return true;
e = endswith(name, ".slice");
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index abb0b76866..17a9b5a8f1 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -137,7 +137,8 @@ int get_user_creds(
return 0;
}
- if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) {
+ if (synthesize_nobody() &&
+ STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) {
*username = NOBODY_USER_NAME;
if (uid)
@@ -243,7 +244,8 @@ int get_group_creds(const char **groupname, gid_t *gid) {
return 0;
}
- if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) {
+ if (synthesize_nobody() &&
+ STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) {
*groupname = NOBODY_GROUP_NAME;
if (gid)
@@ -283,7 +285,8 @@ char* uid_to_name(uid_t uid) {
/* Shortcut things to avoid NSS lookups */
if (uid == 0)
return strdup("root");
- if (uid == UID_NOBODY)
+ if (synthesize_nobody() &&
+ uid == UID_NOBODY)
return strdup(NOBODY_USER_NAME);
if (uid_is_valid(uid)) {
@@ -323,7 +326,8 @@ char* gid_to_name(gid_t gid) {
if (gid == 0)
return strdup("root");
- if (gid == GID_NOBODY)
+ if (synthesize_nobody() &&
+ gid == GID_NOBODY)
return strdup(NOBODY_GROUP_NAME);
if (gid_is_valid(gid)) {
@@ -358,8 +362,9 @@ char* gid_to_name(gid_t gid) {
}
int in_gid(gid_t gid) {
+ long ngroups_max;
gid_t *gids;
- int ngroups_max, r, i;
+ int r, i;
if (getgid() == gid)
return 1;
@@ -373,7 +378,7 @@ int in_gid(gid_t gid) {
ngroups_max = sysconf(_SC_NGROUPS_MAX);
assert(ngroups_max > 0);
- gids = alloca(sizeof(gid_t) * ngroups_max);
+ gids = newa(gid_t, ngroups_max);
r = getgroups(ngroups_max, gids);
if (r < 0)
@@ -426,7 +431,8 @@ int get_home_dir(char **_h) {
*_h = h;
return 0;
}
- if (u == UID_NOBODY) {
+ if (synthesize_nobody() &&
+ u == UID_NOBODY) {
h = strdup("/");
if (!h)
return -ENOMEM;
@@ -481,7 +487,8 @@ int get_shell(char **_s) {
*_s = s;
return 0;
}
- if (u == UID_NOBODY) {
+ if (synthesize_nobody() &&
+ u == UID_NOBODY) {
s = strdup("/sbin/nologin");
if (!s)
return -ENOMEM;
@@ -689,3 +696,24 @@ int maybe_setgroups(size_t size, const gid_t *list) {
return 0;
}
+
+bool synthesize_nobody(void) {
+
+#ifdef NOLEGACY
+ return true;
+#else
+ /* Returns true when we shall synthesize the "nobody" user (which we do by default). This can be turned off by
+ * touching /etc/systemd/dont-synthesize-nobody in order to provide upgrade compatibility with legacy systems
+ * that used the "nobody" user name and group name for other UIDs/GIDs than 65534.
+ *
+ * Note that we do not employ any kind of synchronization on the following caching variable. If the variable is
+ * accessed in multi-threaded programs in the worst case it might happen that we initialize twice, but that
+ * shouldn't matter as each initialization should come to the same result. */
+ static int cache = -1;
+
+ if (cache < 0)
+ cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
+
+ return cache;
+#endif
+}
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
index 79adf91ee9..5f0391f2b8 100644
--- a/src/basic/user-util.h
+++ b/src/basic/user-util.h
@@ -97,3 +97,5 @@ bool valid_gecos(const char *d);
bool valid_home(const char *p);
int maybe_setgroups(size_t size, const gid_t *list);
+
+bool synthesize_nobody(void);
diff --git a/src/basic/util.c b/src/basic/util.c
index 8f9f2b902b..c7f1513f3e 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -52,6 +52,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "procfs-util.h"
#include "set.h"
#include "signal-util.h"
#include "stat-util.h"
@@ -61,6 +62,7 @@
#include "umask-util.h"
#include "user-util.h"
#include "util.h"
+#include "virt.h"
int saved_argc = 0;
char **saved_argv = NULL;
@@ -118,45 +120,6 @@ int socket_from_display(const char *display, char **path) {
return 0;
}
-int block_get_whole_disk(dev_t d, dev_t *ret) {
- char p[SYS_BLOCK_PATH_MAX("/partition")];
- _cleanup_free_ char *s = NULL;
- int r;
- unsigned n, m;
-
- assert(ret);
-
- /* If it has a queue this is good enough for us */
- xsprintf_sys_block_path(p, "/queue", d);
- if (access(p, F_OK) >= 0) {
- *ret = d;
- return 0;
- }
-
- /* If it is a partition find the originating device */
- xsprintf_sys_block_path(p, "/partition", d);
- if (access(p, F_OK) < 0)
- return -ENOENT;
-
- /* Get parent dev_t */
- xsprintf_sys_block_path(p, "/../dev", d);
- r = read_one_line_file(p, &s);
- if (r < 0)
- return r;
-
- r = sscanf(s, "%u:%u", &m, &n);
- if (r != 2)
- return -EINVAL;
-
- /* Only return this if it is really good enough for us. */
- xsprintf_sys_block_path(p, "/queue", makedev(m, n));
- if (access(p, F_OK) < 0)
- return -ENOENT;
-
- *ret = makedev(m, n);
- return 0;
-}
-
bool kexec_loaded(void) {
_cleanup_free_ char *s = NULL;
@@ -184,112 +147,6 @@ int prot_from_flags(int flags) {
}
}
-int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
- bool stdout_is_tty, stderr_is_tty;
- pid_t parent_pid, agent_pid;
- sigset_t ss, saved_ss;
- unsigned n, i;
- va_list ap;
- char **l;
-
- assert(pid);
- assert(path);
-
- /* Spawns a temporary TTY agent, making sure it goes away when
- * we go away */
-
- parent_pid = getpid_cached();
-
- /* First we temporarily block all signals, so that the new
- * child has them blocked initially. This way, we can be sure
- * that SIGTERMs are not lost we might send to the agent. */
- assert_se(sigfillset(&ss) >= 0);
- assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
-
- agent_pid = fork();
- if (agent_pid < 0) {
- assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
- return -errno;
- }
-
- if (agent_pid != 0) {
- assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
- *pid = agent_pid;
- return 0;
- }
-
- /* In the child:
- *
- * Make sure the agent goes away when the parent dies */
- if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
- _exit(EXIT_FAILURE);
-
- /* Make sure we actually can kill the agent, if we need to, in
- * case somebody invoked us from a shell script that trapped
- * SIGTERM or so... */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- /* Check whether our parent died before we were able
- * to set the death signal and unblock the signals */
- if (getppid() != parent_pid)
- _exit(EXIT_SUCCESS);
-
- /* Don't leak fds to the agent */
- close_all_fds(except, n_except);
-
- stdout_is_tty = isatty(STDOUT_FILENO);
- stderr_is_tty = isatty(STDERR_FILENO);
-
- if (!stdout_is_tty || !stderr_is_tty) {
- int fd;
-
- /* Detach from stdout/stderr. and reopen
- * /dev/tty for them. This is important to
- * ensure that when systemctl is started via
- * popen() or a similar call that expects to
- * read EOF we actually do generate EOF and
- * not delay this indefinitely by because we
- * keep an unused copy of stdin around. */
- fd = open("/dev/tty", O_WRONLY);
- if (fd < 0) {
- log_error_errno(errno, "Failed to open /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
- log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
- log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (fd > STDERR_FILENO)
- close(fd);
- }
-
- /* Count arguments */
- va_start(ap, path);
- for (n = 0; va_arg(ap, char*); n++)
- ;
- va_end(ap);
-
- /* Allocate strv */
- l = alloca(sizeof(char *) * (n + 1));
-
- /* Fill in arguments */
- va_start(ap, path);
- for (i = 0; i <= n; i++)
- l[i] = va_arg(ap, char*);
- va_end(ap);
-
- execv(path, l);
- _exit(EXIT_FAILURE);
-}
-
bool in_initrd(void) {
struct statfs s;
@@ -617,31 +474,22 @@ uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
uint64_t system_tasks_max(void) {
-#if SIZEOF_PID_T == 4
-#define TASKS_MAX ((uint64_t) (INT32_MAX-1))
-#elif SIZEOF_PID_T == 2
-#define TASKS_MAX ((uint64_t) (INT16_MAX-1))
-#else
-#error "Unknown pid_t size"
-#endif
-
- _cleanup_free_ char *value = NULL, *root = NULL;
uint64_t a = TASKS_MAX, b = TASKS_MAX;
+ _cleanup_free_ char *root = NULL;
/* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
* limit:
*
- * a) the maximum value for the pid_t type
+ * a) the maximum tasks value the kernel allows on this architecture
* b) the cgroups pids_max attribute for the system
- * c) the kernel's configure maximum PID value
+ * c) the kernel's configured maximum PID value
*
* And then pick the smallest of the three */
- if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0)
- (void) safe_atou64(value, &a);
+ (void) procfs_tasks_get_limit(&a);
if (cg_get_root_path(&root) >= 0) {
- value = mfree(value);
+ _cleanup_free_ char *value = NULL;
if (cg_get_attribute("pids", root, "pids.max", &value) >= 0)
(void) safe_atou64(value, &b);
@@ -699,131 +547,76 @@ int version(void) {
return 0;
}
-int get_block_device(const char *path, dev_t *dev) {
- struct stat st;
- struct statfs sfs;
-
- assert(path);
- assert(dev);
-
- /* Get's the block device directly backing a file system. If
- * the block device is encrypted, returns the device mapper
- * block device. */
-
- if (lstat(path, &st))
- return -errno;
-
- if (major(st.st_dev) != 0) {
- *dev = st.st_dev;
- return 1;
- }
-
- if (statfs(path, &sfs) < 0)
- return -errno;
-
- if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
- return btrfs_get_block_device(path, dev);
-
- return 0;
+/* This is a direct translation of str_verscmp from boot.c */
+static bool is_digit(int c) {
+ return c >= '0' && c <= '9';
}
-int get_block_device_harder(const char *path, dev_t *dev) {
- _cleanup_closedir_ DIR *d = NULL;
- _cleanup_free_ char *t = NULL;
- char p[SYS_BLOCK_PATH_MAX("/slaves")];
- struct dirent *de, *found = NULL;
- const char *q;
- unsigned maj, min;
- dev_t dt;
- int r;
-
- assert(path);
- assert(dev);
+static int c_order(int c) {
+ if (c == 0 || is_digit(c))
+ return 0;
- /* Gets the backing block device for a file system, and
- * handles LUKS encrypted file systems, looking for its
- * immediate parent, if there is one. */
+ if ((c >= 'a') && (c <= 'z'))
+ return c;
- r = get_block_device(path, &dt);
- if (r <= 0)
- return r;
+ return c + 0x10000;
+}
- xsprintf_sys_block_path(p, "/slaves", dt);
- d = opendir(p);
- if (!d) {
- if (errno == ENOENT)
- goto fallback;
+int str_verscmp(const char *s1, const char *s2) {
+ const char *os1, *os2;
- return -errno;
- }
+ assert(s1);
+ assert(s2);
- FOREACH_DIRENT_ALL(de, d, return -errno) {
+ os1 = s1;
+ os2 = s2;
- if (dot_or_dot_dot(de->d_name))
- continue;
+ while (*s1 || *s2) {
+ int first;
- if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
- continue;
+ while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
+ int order;
- if (found) {
- _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
-
- /* We found a device backed by multiple other devices. We don't really support automatic
- * discovery on such setups, with the exception of dm-verity partitions. In this case there are
- * two backing devices: the data partition and the hash partition. We are fine with such
- * setups, however, only if both partitions are on the same physical device. Hence, let's
- * verify this. */
-
- u = strjoin(p, "/", de->d_name, "/../dev");
- if (!u)
- return -ENOMEM;
-
- v = strjoin(p, "/", found->d_name, "/../dev");
- if (!v)
- return -ENOMEM;
-
- r = read_one_line_file(u, &a);
- if (r < 0) {
- log_debug_errno(r, "Failed to read %s: %m", u);
- goto fallback;
- }
-
- r = read_one_line_file(v, &b);
- if (r < 0) {
- log_debug_errno(r, "Failed to read %s: %m", v);
- goto fallback;
- }
-
- /* Check if the parent device is the same. If not, then the two backing devices are on
- * different physical devices, and we don't support that. */
- if (!streq(a, b))
- goto fallback;
+ order = c_order(*s1) - c_order(*s2);
+ if (order != 0)
+ return order;
+ s1++;
+ s2++;
}
- found = de;
- }
-
- if (!found)
- goto fallback;
+ while (*s1 == '0')
+ s1++;
+ while (*s2 == '0')
+ s2++;
+
+ first = 0;
+ while (is_digit(*s1) && is_digit(*s2)) {
+ if (first == 0)
+ first = *s1 - *s2;
+ s1++;
+ s2++;
+ }
- q = strjoina(p, "/", found->d_name, "/dev");
+ if (is_digit(*s1))
+ return 1;
+ if (is_digit(*s2))
+ return -1;
- r = read_one_line_file(q, &t);
- if (r == -ENOENT)
- goto fallback;
- if (r < 0)
- return r;
+ if (first != 0)
+ return first;
+ }
- if (sscanf(t, "%u:%u", &maj, &min) != 2)
- return -EINVAL;
+ return strcmp(os1, os2);
+}
- if (maj == 0)
- goto fallback;
+/* Turn off core dumps but only if we're running outside of a container. */
+void disable_coredumps(void) {
+ int r;
- *dev = makedev(maj, min);
- return 1;
+ if (detect_container() > 0)
+ return;
-fallback:
- *dev = dt;
- return 1;
+ r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
+ if (r < 0)
+ log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
}
diff --git a/src/basic/util.h b/src/basic/util.h
index a79907de3e..9d1b10756b 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -71,8 +71,6 @@ bool plymouth_running(void);
bool display_is_local(const char *display) _pure_;
int socket_from_display(const char *display, char **path);
-int block_get_whole_disk(dev_t d, dev_t *ret);
-
#define NULSTR_FOREACH(i, l) \
for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
@@ -86,8 +84,6 @@ bool kexec_loaded(void);
int prot_from_flags(int flags) _const_;
-int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...);
-
bool in_initrd(void);
void in_initrd_force(bool value);
@@ -194,5 +190,6 @@ int update_reboot_parameter_and_warn(const char *param);
int version(void);
-int get_block_device(const char *path, dev_t *dev);
-int get_block_device_harder(const char *path, dev_t *dev);
+int str_verscmp(const char *s1, const char *s2);
+
+void disable_coredumps(void);
diff --git a/src/basic/verbs.c b/src/basic/verbs.c
index cb42e6dd08..47644670da 100644
--- a/src/basic/verbs.c
+++ b/src/basic/verbs.c
@@ -22,13 +22,48 @@
#include <getopt.h>
#include <stdbool.h>
#include <stddef.h>
+#include <string.h>
+#include "env-util.h"
#include "log.h"
#include "macro.h"
+#include "process-util.h"
#include "string-util.h"
#include "verbs.h"
#include "virt.h"
+/* Wraps running_in_chroot() which is used in various places, but also adds an environment variable check so external
+ * processes can reliably force this on.
+ */
+bool running_in_chroot_or_offline(void) {
+ int r;
+
+ /* Added to support use cases like rpm-ostree, where from %post scripts we only want to execute "preset", but
+ * not "start"/"restart" for example.
+ *
+ * See ENVIRONMENT.md for docs.
+ */
+ r = getenv_bool("SYSTEMD_OFFLINE");
+ if (r < 0 && r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_OFFLINE: %m");
+ else if (r >= 0)
+ return r > 0;
+
+ /* We've had this condition check for a long time which basically checks for legacy chroot case like Fedora's
+ * "mock", which is used for package builds. We don't want to try to start systemd services there, since
+ * without --new-chroot we don't even have systemd running, and even if we did, adding a concept of background
+ * daemons to builds would be an enormous change, requiring considering things like how the journal output is
+ * handled, etc. And there's really not a use case today for a build talking to a service.
+ *
+ * Note this call itself also looks for a different variable SYSTEMD_IGNORE_CHROOT=1.
+ */
+ r = running_in_chroot();
+ if (r < 0)
+ log_debug_errno(r, "running_in_chroot(): %m");
+
+ return r > 0;
+}
+
int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
const Verb *verb;
const char *name;
@@ -84,12 +119,15 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
return -EINVAL;
}
- if ((verb->flags & VERB_NOCHROOT) && running_in_chroot() > 0) {
- log_info("Running in chroot, ignoring request.");
+ if ((verb->flags & VERB_ONLINE_ONLY) && running_in_chroot_or_offline()) {
+ if (name)
+ log_info("Running in chroot, ignoring request: %s", name);
+ else
+ log_info("Running in chroot, ignoring request.");
return 0;
}
- if (verb->flags & VERB_MUSTBEROOT) {
+ if (verb->flags & VERB_MUST_BE_ROOT) {
r = must_be_root();
if (r < 0)
return r;
diff --git a/src/basic/verbs.h b/src/basic/verbs.h
index 5f44a18f8e..d9259fc45f 100644
--- a/src/basic/verbs.h
+++ b/src/basic/verbs.h
@@ -23,9 +23,9 @@
#define VERB_ANY ((unsigned) -1)
typedef enum VerbFlags {
- VERB_DEFAULT = 1 << 0,
- VERB_NOCHROOT = 1 << 1,
- VERB_MUSTBEROOT = 1 << 2,
+ VERB_DEFAULT = 1 << 0,
+ VERB_ONLINE_ONLY = 1 << 1,
+ VERB_MUST_BE_ROOT = 1 << 2,
} VerbFlags;
typedef struct {
@@ -35,4 +35,6 @@ typedef struct {
int (* const dispatch)(int argc, char *argv[], void *userdata);
} Verb;
+bool running_in_chroot_or_offline(void);
+
int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata);
diff --git a/src/basic/virt.c b/src/basic/virt.c
index b0db28add6..f4796b53bc 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -18,6 +18,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#if defined(__i386__) || defined(__x86_64__)
+#include <cpuid.h>
+#endif
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
@@ -56,30 +59,14 @@ static int detect_vm_cpuid(void) {
{ "bhyve bhyve ", VIRTUALIZATION_BHYVE },
};
- uint32_t eax, ecx;
+ uint32_t eax, ebx, ecx, edx;
bool hypervisor;
/* http://lwn.net/Articles/301888/ */
-#if defined (__i386__)
-#define REG_a "eax"
-#define REG_b "ebx"
-#elif defined (__amd64__)
-#define REG_a "rax"
-#define REG_b "rbx"
-#endif
-
/* First detect whether there is a hypervisor */
- eax = 1;
- __asm__ __volatile__ (
- /* ebx/rbx is being used for PIC! */
- " push %%"REG_b" \n\t"
- " cpuid \n\t"
- " pop %%"REG_b" \n\t"
-
- : "=a" (eax), "=c" (ecx)
- : "0" (eax)
- );
+ if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
+ return VIRTUALIZATION_NONE;
hypervisor = !!(ecx & 0x80000000U);
@@ -91,17 +78,11 @@ static int detect_vm_cpuid(void) {
unsigned j;
/* There is a hypervisor, see what it is */
- eax = 0x40000000U;
- __asm__ __volatile__ (
- /* ebx/rbx is being used for PIC! */
- " push %%"REG_b" \n\t"
- " cpuid \n\t"
- " mov %%ebx, %1 \n\t"
- " pop %%"REG_b" \n\t"
-
- : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
- : "0" (eax)
- );
+ __cpuid(0x40000000U, eax, ebx, ecx, edx);
+
+ sig.sig32[0] = ebx;
+ sig.sig32[1] = ecx;
+ sig.sig32[2] = edx;
log_debug("Virtualization found, CPUID=%s", sig.text);
@@ -241,8 +222,10 @@ static int detect_vm_xen_dom0(void) {
if (r == 0) {
unsigned long features;
- r = safe_atolu(domcap, &features);
- if (r == 0) {
+ /* Here, we need to use sscanf() instead of safe_atoul()
+ * as the string lacks the leading "0x". */
+ r = sscanf(domcap, "%lx", &features);
+ if (r == 1) {
r = !!(features & (1U << XENFEAT_dom0));
log_debug("Virtualization XEN, found %s with value %08lx, "
"XENFEAT_dom0 (indicating the 'hardware domain') is%s set.",
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 59c1af73de..ae034f5cdb 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -36,6 +36,8 @@
#include <sys/statfs.h>
#include <unistd.h>
+#include "sd-id128.h"
+
#include "alloc-util.h"
#include "blkid-util.h"
#include "bootspec.h"
@@ -294,7 +296,7 @@ static int status_entries(const char *esp_path, sd_id128_t partition) {
esp_path);
if (config.default_entry < 0)
- printf("%zu entries, no entry suitable as default", config.n_entries);
+ printf("%zu entries, no entry suitable as default\n", config.n_entries);
else {
const BootEntry *e = &config.entries[config.default_entry];
@@ -948,12 +950,13 @@ static int verb_status(int argc, char *argv[], void *userdata) {
* can show */
if (is_efi_boot()) {
- _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL;
+ _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
sd_id128_t loader_part_uuid = SD_ID128_NULL;
read_loader_efi_var("LoaderFirmwareType", &fw_type);
read_loader_efi_var("LoaderFirmwareInfo", &fw_info);
read_loader_efi_var("LoaderInfo", &loader);
+ read_loader_efi_var("StubInfo", &stub);
read_loader_efi_var("LoaderImageIdentifier", &loader_path);
if (loader_path)
@@ -981,6 +984,8 @@ static int verb_status(int argc, char *argv[], void *userdata) {
printf("Current Loader:\n");
printf(" Product: %s\n", strna(loader));
+ if (stub)
+ printf(" Stub: %s\n", stub);
if (!sd_id128_is_null(loader_part_uuid))
printf(" ESP: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
SD_ID128_FORMAT_VAL(loader_part_uuid));
@@ -1043,7 +1048,7 @@ static int verb_list(int argc, char *argv[], void *userdata) {
boot_entry_title(e),
ansi_normal(),
ansi_highlight_green(),
- n == config.default_entry ? " (default)" : "",
+ n == (unsigned) config.default_entry ? " (default)" : "",
ansi_normal());
if (e->version)
printf(" version: %s\n", e->version);
@@ -1139,12 +1144,12 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
static int bootctl_main(int argc, char *argv[]) {
static const Verb verbs[] = {
- { "help", VERB_ANY, VERB_ANY, 0, help },
- { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
- { "list", VERB_ANY, 1, 0, verb_list },
- { "install", VERB_ANY, 1, VERB_MUSTBEROOT, verb_install },
- { "update", VERB_ANY, 1, VERB_MUSTBEROOT, verb_install },
- { "remove", VERB_ANY, 1, VERB_MUSTBEROOT, verb_remove },
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
+ { "list", VERB_ANY, 1, 0, verb_list },
+ { "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
+ { "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
+ { "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove },
{}
};
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index ea9f39a7e7..06331da2d4 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -166,7 +166,7 @@ static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN
case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
/* forward-word */
- while (line[first + cursor] && line[first + cursor] == ' ')
+ while (line[first + cursor] == ' ')
cursor_right(&cursor, &first, x_max, len);
while (line[first + cursor] && line[first + cursor] != ' ')
cursor_right(&cursor, &first, x_max, len);
diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c
index be4fea84a2..5aaffe8fa5 100644
--- a/src/boot/efi/measure.c
+++ b/src/boot/efi/measure.c
@@ -232,8 +232,11 @@ static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 p
*/
static EFI_STATUS trigger_tcg2_final_events_table(const EFI_TCG2 *tcg, EFI_TCG2_EVENT_LOG_FORMAT log_fmt)
{
+ EFI_PHYSICAL_ADDRESS loc;
+ EFI_PHYSICAL_ADDRESS last_loc;
+ BOOLEAN truncated;
return uefi_call_wrapper(tcg->GetEventLog, 5, (EFI_TCG2 *) tcg,
- log_fmt, 0, 0, 0);
+ log_fmt, &loc, &last_loc, &truncated);
}
static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer,
diff --git a/src/boot/efi/no-undefined-symbols.sh b/src/boot/efi/no-undefined-symbols.sh
index 08b266c455..8572ceedfa 100755
--- a/src/boot/efi/no-undefined-symbols.sh
+++ b/src/boot/efi/no-undefined-symbols.sh
@@ -1,4 +1,5 @@
-#!/bin/sh -eu
+#!/bin/sh
+set -eu
if nm -D -u "$1" | grep ' U '; then
echo "Undefined symbols detected!"
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index e917019c0c..ff45cebd45 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -104,6 +104,30 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
+ /* if LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from UEFI */
+ if (efivar_get_raw(&global_guid, L"LoaderImageIdentifier", &b, &size) != EFI_SUCCESS) {
+ CHAR16 *loaded_image_path = DevicePathToStr(loaded_image->FilePath);
+ efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE);
+ FreePool(loaded_image_path);
+ }
+
+ /* if LoaderFirmwareInfo is not set, let's set it */
+ if (efivar_get_raw(&global_guid, L"LoaderFirmwareInfo", &b, &size) != EFI_SUCCESS) {
+ CHAR16 *loader_firmware_info = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+ efivar_set(L"LoaderFirmwareInfo", loader_firmware_info, FALSE);
+ FreePool(loader_firmware_info);
+ }
+ /* ditto for LoaderFirmwareType */
+ if (efivar_get_raw(&global_guid, L"LoaderFirmwareType", &b, &size) != EFI_SUCCESS) {
+ CHAR16 *loader_firmware_type = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+ efivar_set(L"LoaderFirmwareType", loader_firmware_type, FALSE);
+ FreePool(loader_firmware_type);
+ }
+
+ /* add StubInfo */
+ if (efivar_get_raw(&global_guid, L"StubInfo", &b, &size) != EFI_SUCCESS)
+ efivar_set(L"StubInfo", L"systemd-stub " PACKAGE_VERSION, FALSE);
+
if (szs[3] > 0)
graphics_splash((UINT8 *)((UINTN)loaded_image->ImageBase + addrs[3]), szs[3], NULL);
diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c
index 7a8d6ba5ac..f8c43b5079 100644
--- a/src/busctl/busctl.c
+++ b/src/busctl/busctl.c
@@ -62,6 +62,7 @@ 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 bool arg_watch_bind = false;
static usec_t arg_timeout = 0;
#define NAME_IS_ACQUIRED INT_TO_PTR(1)
@@ -1735,7 +1736,9 @@ static int help(void) {
" --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"
+ " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n"
+ " --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n"
+ " system\n\n"
"Commands:\n"
" list List bus names\n"
" status [SERVICE] Show bus service, process or bus owner credentials\n"
@@ -1777,6 +1780,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_ALLOW_INTERACTIVE_AUTHORIZATION,
ARG_TIMEOUT,
ARG_AUGMENT_CREDS,
+ ARG_WATCH_BIND,
};
static const struct option options[] = {
@@ -1803,6 +1807,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
{ "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS},
+ { "watch-bind", required_argument, NULL, ARG_WATCH_BIND },
{},
};
@@ -1953,6 +1958,16 @@ static int parse_argv(int argc, char *argv[]) {
arg_augment_creds = !!r;
break;
+ case ARG_WATCH_BIND:
+ r = parse_boolean(optarg);
+ if (r < 0) {
+ log_error("Failed to parse --watch-bind= parameter.");
+ return r;
+ }
+
+ arg_watch_bind = !!r;
+ break;
+
case '?':
return -EINVAL;
@@ -2002,7 +2017,7 @@ static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ sd_bus *bus = NULL;
int r;
log_parse_environment();
@@ -2051,6 +2066,12 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ r = sd_bus_set_watch_bind(bus, arg_watch_bind);
+ if (r < 0) {
+ log_error_errno(r, "Failed to set watch-bind setting to '%s': %m", yes_no(arg_watch_bind));
+ goto finish;
+ }
+
if (arg_address)
r = sd_bus_set_address(bus, arg_address);
else {
@@ -2092,6 +2113,9 @@ int main(int argc, char *argv[]) {
r = busctl_main(bus, argc, argv);
finish:
+ /* make sure we terminate the bus connection first, and then close the
+ * pager, see issue #3543 for the details. */
+ sd_bus_flush_close_unref(bus);
pager_close();
strv_free(arg_matches);
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index fb44b9f669..bd8c6a0059 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -277,9 +277,9 @@ int main(int argc, char *argv[]) {
if (!arg_machine) {
_cleanup_free_ char *cwd = NULL;
- cwd = get_current_dir_name();
- if (!cwd) {
- r = log_error_errno(errno, "Cannot determine current working directory: %m");
+ r = safe_getcwd(&cwd);
+ if (r < 0) {
+ log_error_errno(r, "Cannot determine current working directory: %m");
goto finish;
}
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index fe339eb493..1a73fb099d 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -40,7 +40,9 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "procfs-util.h"
#include "stdio-util.h"
+#include "strv.h"
#include "terminal-util.h"
#include "unit-name.h"
#include "util.h"
@@ -193,26 +195,33 @@ static int process(
g->n_tasks_valid = true;
} else if (streq(controller, "pids") && arg_count == COUNT_PIDS) {
- _cleanup_free_ char *p = NULL, *v = NULL;
- r = cg_get_path(controller, path, "pids.current", &p);
- if (r < 0)
- return r;
+ if (isempty(path) || path_equal(path, "/")) {
+ r = procfs_tasks_get_current(&g->n_tasks);
+ if (r < 0)
+ return r;
+ } else {
+ _cleanup_free_ char *p = NULL, *v = NULL;
- r = read_one_line_file(p, &v);
- if (r == -ENOENT)
- return 0;
- if (r < 0)
- return r;
+ r = cg_get_path(controller, path, "pids.current", &p);
+ if (r < 0)
+ return r;
- r = safe_atou64(v, &g->n_tasks);
- if (r < 0)
- return r;
+ r = read_one_line_file(p, &v);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(v, &g->n_tasks);
+ if (r < 0)
+ return r;
+ }
if (g->n_tasks > 0)
g->n_tasks_valid = true;
- } else if (streq(controller, "cpu") || streq(controller, "cpuacct")) {
+ } else if (STR_IN_SET(controller, "cpu", "cpuacct")) {
_cleanup_free_ char *p = NULL, *v = NULL;
uint64_t new_usage;
nsec_t timestamp;
diff --git a/src/core/automount.c b/src/core/automount.c
index 28d5cc3917..c191336c0e 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -578,7 +578,7 @@ static void automount_enter_waiting(Automount *a) {
set_clear(a->tokens);
- r = unit_fail_if_symlink(UNIT(a), a->where);
+ r = unit_fail_if_noncanonical(UNIT(a), a->where);
if (r < 0)
goto fail;
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 78ef885b06..97b3756567 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -22,6 +22,7 @@
#include <fnmatch.h>
#include "alloc-util.h"
+#include "blockdev-util.h"
#include "bpf-firewall.h"
#include "cgroup-util.h"
#include "cgroup.h"
@@ -31,6 +32,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "procfs-util.h"
#include "special.h"
#include "stdio-util.h"
#include "string-table.h"
@@ -38,6 +40,18 @@
#define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC)
+bool unit_has_root_cgroup(Unit *u) {
+ assert(u);
+
+ /* Returns whether this unit manages the root cgroup. Note that this is different from being named "-.slice",
+ * as inside of containers the root slice won't be identical to the root cgroup. */
+
+ if (!u->cgroup_path)
+ return false;
+
+ return isempty(u->cgroup_path) || path_equal(u->cgroup_path, "/");
+}
+
static void cgroup_compat_warn(void) {
static bool cgroup_compat_warned = false;
@@ -307,7 +321,7 @@ static int lookup_block_device(const char *p, dev_t *dev) {
/* If this is a partition, try to get the originating
* block device */
- block_get_whole_disk(*dev, dev);
+ (void) block_get_whole_disk(*dev, dev);
} else {
log_warning("%s is not a block device and file system block device cannot be determined or is not local.", p);
return -ENODEV;
@@ -707,21 +721,17 @@ static void cgroup_context_apply(
assert(u);
- c = unit_get_cgroup_context(u);
- path = u->cgroup_path;
-
- assert(c);
- assert(path);
-
/* Nothing to do? Exit early! */
if (apply_mask == 0 && !apply_bpf)
return;
- /* Some cgroup attributes are not supported on the root cgroup,
- * hence silently ignore */
- is_root = isempty(path) || path_equal(path, "/");
- if (is_root)
- /* Make sure we don't try to display messages with an empty path. */
+ /* Some cgroup attributes are not supported on the root cgroup, hence silently ignore */
+ is_root = unit_has_root_cgroup(u);
+
+ assert_se(c = unit_get_cgroup_context(u));
+ assert_se(path = u->cgroup_path);
+
+ if (is_root) /* Make sure we don't try to display messages with an empty path. */
path = "/";
/* We generally ignore errors caused by read-only mounted
@@ -977,7 +987,7 @@ static void cgroup_context_apply(
"/dev/random\0" "rwm\0"
"/dev/urandom\0" "rwm\0"
"/dev/tty\0" "rwm\0"
- "/dev/pts/ptmx\0" "rw\0" /* /dev/pts/ptmx may not be duplicated, but accessed */
+ "/dev/ptmx\0" "rwm\0"
/* Allow /run/systemd/inaccessible/{chr,blk} devices for mapping InaccessiblePaths */
"-/run/systemd/inaccessible/chr\0" "rwm\0"
"-/run/systemd/inaccessible/blk\0" "rwm\0";
@@ -987,6 +997,7 @@ static void cgroup_context_apply(
NULSTR_FOREACH_PAIR(x, y, auto_devices)
whitelist_device(path, x, y);
+ /* PTS (/dev/pts) devices may not be duplicated, but accessed */
whitelist_major(path, "pts", 'c', "rw");
}
@@ -1017,19 +1028,46 @@ static void cgroup_context_apply(
}
}
- if ((apply_mask & CGROUP_MASK_PIDS) && !is_root) {
+ if (apply_mask & CGROUP_MASK_PIDS) {
+
+ if (is_root) {
+ /* So, the "pids" controller does not expose anything on the root cgroup, in order not to
+ * replicate knobs exposed elsewhere needlessly. We abstract this away here however, and when
+ * the knobs of the root cgroup are modified propagate this to the relevant sysctls. There's a
+ * non-obvious asymmetry however: unlike the cgroup properties we don't really want to take
+ * exclusive ownership of the sysctls, but we still want to honour things if the user sets
+ * limits. Hence we employ sort of a one-way strategy: when the user sets a bounded limit
+ * through us it counts. When the user afterwards unsets it again (i.e. sets it to unbounded)
+ * it also counts. But if the user never set a limit through us (i.e. we are the default of
+ * "unbounded") we leave things unmodified. For this we manage a global boolean that we turn on
+ * the first time we set a limit. Note that this boolean is flushed out on manager reload,
+ * which is desirable so that there's an offical way to release control of the sysctl from
+ * systemd: set the limit to unbounded and reload. */
+
+ if (c->tasks_max != CGROUP_LIMIT_MAX) {
+ u->manager->sysctl_pid_max_changed = true;
+ r = procfs_tasks_set_limit(c->tasks_max);
+ } else if (u->manager->sysctl_pid_max_changed)
+ r = procfs_tasks_set_limit(TASKS_MAX);
+ else
+ r = 0;
- if (c->tasks_max != CGROUP_LIMIT_MAX) {
- char buf[DECIMAL_STR_MAX(uint64_t) + 2];
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to write to tasks limit sysctls: %m");
- sprintf(buf, "%" PRIu64 "\n", c->tasks_max);
- r = cg_set_attribute("pids", path, "pids.max", buf);
- } else
- r = cg_set_attribute("pids", path, "pids.max", "max");
+ } else {
+ if (c->tasks_max != CGROUP_LIMIT_MAX) {
+ char buf[DECIMAL_STR_MAX(uint64_t) + 2];
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set pids.max: %m");
+ sprintf(buf, "%" PRIu64 "\n", c->tasks_max);
+ r = cg_set_attribute("pids", path, "pids.max", buf);
+ } else
+ r = cg_set_attribute("pids", path, "pids.max", "max");
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set pids.max: %m");
+ }
}
if (apply_bpf)
@@ -1060,7 +1098,7 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) {
mask |= CGROUP_MASK_DEVICES;
if (c->tasks_accounting ||
- c->tasks_max != (uint64_t) -1)
+ c->tasks_max != CGROUP_LIMIT_MAX)
mask |= CGROUP_MASK_PIDS;
return mask;
@@ -1825,6 +1863,31 @@ static int unit_watch_pids_in_path(Unit *u, const char *path) {
return ret;
}
+int unit_synthesize_cgroup_empty_event(Unit *u) {
+ int r;
+
+ assert(u);
+
+ /* Enqueue a synthetic cgroup empty event if this unit doesn't watch any PIDs anymore. This is compatibility
+ * support for non-unified systems where notifications aren't reliable, and hence need to take whatever we can
+ * get as notification source as soon as we stopped having any useful PIDs to watch for. */
+
+ if (!u->cgroup_path)
+ return -ENOENT;
+
+ r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
+ if (r < 0)
+ return r;
+ if (r > 0) /* On unified we have reliable notifications, and don't need this */
+ return 0;
+
+ if (!set_isempty(u->pids))
+ return 0;
+
+ unit_add_to_cgroup_empty_queue(u);
+ return 0;
+}
+
int unit_watch_all_pids(Unit *u) {
int r;
@@ -2159,40 +2222,46 @@ Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid) {
_cleanup_free_ char *cgroup = NULL;
- int r;
assert(m);
- if (pid <= 0)
+ if (!pid_is_valid(pid))
return NULL;
- r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
- if (r < 0)
+ if (cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup) < 0)
return NULL;
return manager_get_unit_by_cgroup(m, cgroup);
}
Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
- Unit *u;
+ Unit *u, **array;
assert(m);
- if (pid <= 0)
+ /* Note that a process might be owned by multiple units, we return only one here, which is good enough for most
+ * cases, though not strictly correct. We prefer the one reported by cgroup membership, as that's the most
+ * relevant one as children of the process will be assigned to that one, too, before all else. */
+
+ if (!pid_is_valid(pid))
return NULL;
- if (pid == 1)
+ if (pid == getpid_cached())
return hashmap_get(m->units, SPECIAL_INIT_SCOPE);
- u = hashmap_get(m->watch_pids1, PID_TO_PTR(pid));
+ u = manager_get_unit_by_pid_cgroup(m, pid);
if (u)
return u;
- u = hashmap_get(m->watch_pids2, PID_TO_PTR(pid));
+ u = hashmap_get(m->watch_pids, PID_TO_PTR(pid));
if (u)
return u;
- return manager_get_unit_by_pid_cgroup(m, pid);
+ array = hashmap_get(m->watch_pids, PID_TO_PTR(-pid));
+ if (array)
+ return array[0];
+
+ return NULL;
}
int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
@@ -2261,6 +2330,10 @@ int unit_get_tasks_current(Unit *u, uint64_t *ret) {
if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0)
return -ENODATA;
+ /* The root cgroup doesn't expose this information, let's get it from /proc instead */
+ if (unit_has_root_cgroup(u))
+ return procfs_tasks_get_current(ret);
+
r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v);
if (r == -ENOENT)
return -ENODATA;
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 0c5bb4a2c8..1f50441412 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -192,6 +192,8 @@ Unit* manager_get_unit_by_pid(Manager *m, pid_t pid);
int unit_search_main_pid(Unit *u, pid_t *ret);
int unit_watch_all_pids(Unit *u);
+int unit_synthesize_cgroup_empty_event(Unit *u);
+
int unit_get_memory_current(Unit *u, uint64_t *ret);
int unit_get_tasks_current(Unit *u, uint64_t *ret);
int unit_get_cpu_usage(Unit *u, nsec_t *ret);
@@ -206,6 +208,8 @@ int unit_reset_ip_accounting(Unit *u);
cc ? cc->name : false; \
})
+bool unit_has_root_cgroup(Unit *u);
+
int manager_notify_cgroup_empty(Manager *m, const char *group);
void unit_invalidate_cgroup(Unit *u, CGroupMask m);
diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c
index 4b1b86f7b9..113d352a43 100644
--- a/src/core/dbus-automount.c
+++ b/src/core/dbus-automount.c
@@ -21,6 +21,7 @@
#include "automount.h"
#include "bus-util.h"
#include "dbus-automount.h"
+#include "dbus-util.h"
#include "string-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, automount_result, AutomountResult);
@@ -41,7 +42,7 @@ static int bus_automount_set_transient_property(
UnitWriteFlags flags,
sd_bus_error *error) {
- int r;
+ Unit *u = UNIT(a);
assert(a);
assert(name);
@@ -49,24 +50,16 @@ static int bus_automount_set_transient_property(
flags |= UNIT_PRIVATE;
- if (streq(name, "TimeoutIdleUSec")) {
- usec_t timeout_idle_usec;
+ if (streq(name, "Where"))
+ return bus_set_transient_path(u, name, &a->where, message, flags, error);
- r = sd_bus_message_read(message, "t", &timeout_idle_usec);
- if (r < 0)
- return r;
+ if (streq(name, "TimeoutIdleUSec"))
+ return bus_set_transient_usec_fix_0(u, name, &a->timeout_idle_usec, message, flags, error);
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- char time[FORMAT_TIMESPAN_MAX];
+ if (streq(name, "DirectoryMode"))
+ return bus_set_transient_mode_t(u, name, &a->directory_mode, message, flags, error);
- a->timeout_idle_usec = timeout_idle_usec;
- unit_write_settingf(UNIT(a), flags, name, "TimeoutIdleSec=%s\n",
- format_timespan(time, sizeof(time), timeout_idle_usec, USEC_PER_MSEC));
- }
- } else
- return 0;
-
- return 1;
+ return 0;
}
int bus_automount_set_property(
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index abca4e112d..f8d90d4b3a 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -28,6 +28,7 @@
#include "cgroup-util.h"
#include "cgroup.h"
#include "dbus-cgroup.h"
+#include "dbus-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "path-util.h"
@@ -413,6 +414,41 @@ static int bus_cgroup_set_transient_property(
return 0;
}
+static int bus_cgroup_set_boolean(
+ Unit *u,
+ const char *name,
+ bool *p,
+ CGroupMask mask,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
+
+ int b, r;
+
+ assert(p);
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ *p = b;
+ unit_invalidate_cgroup(u, mask);
+ unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(b));
+ }
+
+ return 1;
+}
+
+static BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_weight, CGROUP_MASK_CPU, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID,);
+static BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_shares, CGROUP_MASK_CPU, CGROUP_CPU_SHARES_IS_OK, CGROUP_CPU_SHARES_INVALID,);
+static BUS_DEFINE_SET_CGROUP_WEIGHT(io_weight, CGROUP_MASK_IO, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID,);
+static BUS_DEFINE_SET_CGROUP_WEIGHT(blockio_weight, CGROUP_MASK_BLKIO, CGROUP_BLKIO_WEIGHT_IS_OK, CGROUP_BLKIO_WEIGHT_INVALID,);
+static BUS_DEFINE_SET_CGROUP_WEIGHT(memory, CGROUP_MASK_MEMORY, , CGROUP_LIMIT_MAX, "infinity");
+static BUS_DEFINE_SET_CGROUP_WEIGHT(tasks_max, CGROUP_MASK_PIDS, , (uint64_t) -1, "infinity");
+static BUS_DEFINE_SET_CGROUP_SCALE(memory, CGROUP_MASK_MEMORY, physical_memory_scale);
+static BUS_DEFINE_SET_CGROUP_SCALE(tasks_max, CGROUP_MASK_PIDS, system_tasks_max_scale);
+
int bus_cgroup_set_property(
Unit *u,
CGroupContext *c,
@@ -431,110 +467,82 @@ int bus_cgroup_set_property(
flags |= UNIT_PRIVATE;
- if (streq(name, "CPUAccounting")) {
- int b;
-
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->cpu_accounting = b;
- unit_invalidate_cgroup(u, CGROUP_MASK_CPUACCT|CGROUP_MASK_CPU);
- unit_write_settingf(u, flags, name, "CPUAccounting=%s", yes_no(b));
- }
+ if (streq(name, "CPUAccounting"))
+ return bus_cgroup_set_boolean(u, name, &c->cpu_accounting, CGROUP_MASK_CPUACCT|CGROUP_MASK_CPU, message, flags, error);
- return 1;
+ if (streq(name, "CPUWeight"))
+ return bus_cgroup_set_cpu_weight(u, name, &c->cpu_weight, message, flags, error);
- } else if (streq(name, "CPUWeight")) {
- uint64_t weight;
-
- r = sd_bus_message_read(message, "t", &weight);
- if (r < 0)
- return r;
+ if (streq(name, "StartupCPUWeight"))
+ return bus_cgroup_set_cpu_weight(u, name, &c->startup_cpu_weight, message, flags, error);
- if (!CGROUP_WEIGHT_IS_OK(weight))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "CPUWeight= value out of range");
+ if (streq(name, "CPUShares"))
+ return bus_cgroup_set_cpu_shares(u, name, &c->cpu_shares, message, flags, error);
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->cpu_weight = weight;
- unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
+ if (streq(name, "StartupCPUShares"))
+ return bus_cgroup_set_cpu_shares(u, name, &c->startup_cpu_shares, message, flags, error);
- if (weight == CGROUP_WEIGHT_INVALID)
- unit_write_setting(u, flags, name, "CPUWeight=");
- else
- unit_write_settingf(u, flags, name, "CPUWeight=%" PRIu64, weight);
- }
+ if (streq(name, "IOAccounting"))
+ return bus_cgroup_set_boolean(u, name, &c->io_accounting, CGROUP_MASK_IO, message, flags, error);
- return 1;
+ if (streq(name, "IOWeight"))
+ return bus_cgroup_set_io_weight(u, name, &c->io_weight, message, flags, error);
- } else if (streq(name, "StartupCPUWeight")) {
- uint64_t weight;
+ if (streq(name, "StartupIOWeight"))
+ return bus_cgroup_set_io_weight(u, name, &c->startup_io_weight, message, flags, error);
- r = sd_bus_message_read(message, "t", &weight);
- if (r < 0)
- return r;
+ if (streq(name, "BlockIOAccounting"))
+ return bus_cgroup_set_boolean(u, name, &c->blockio_accounting, CGROUP_MASK_BLKIO, message, flags, error);
- if (!CGROUP_WEIGHT_IS_OK(weight))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "StartupCPUWeight= value out of range");
+ if (streq(name, "BlockIOWeight"))
+ return bus_cgroup_set_blockio_weight(u, name, &c->blockio_weight, message, flags, error);
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->startup_cpu_weight = weight;
- unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
+ if (streq(name, "StartupBlockIOWeight"))
+ return bus_cgroup_set_blockio_weight(u, name, &c->startup_blockio_weight, message, flags, error);
- if (weight == CGROUP_CPU_SHARES_INVALID)
- unit_write_setting(u, flags, name, "StartupCPUWeight=");
- else
- unit_write_settingf(u, flags, name, "StartupCPUWeight=%" PRIu64, weight);
- }
+ if (streq(name, "MemoryAccounting"))
+ return bus_cgroup_set_boolean(u, name, &c->memory_accounting, CGROUP_MASK_MEMORY, message, flags, error);
- return 1;
+ if (streq(name, "MemoryLow"))
+ return bus_cgroup_set_memory(u, name, &c->memory_low, message, flags, error);
- } else if (streq(name, "CPUShares")) {
- uint64_t shares;
+ if (streq(name, "MemoryHigh"))
+ return bus_cgroup_set_memory(u, name, &c->memory_high, message, flags, error);
- r = sd_bus_message_read(message, "t", &shares);
- if (r < 0)
- return r;
+ if (streq(name, "MemorySwapMax"))
+ return bus_cgroup_set_memory(u, name, &c->memory_swap_max, message, flags, error);
- if (!CGROUP_CPU_SHARES_IS_OK(shares))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "CPUShares= value out of range");
+ if (streq(name, "MemoryMax"))
+ return bus_cgroup_set_memory(u, name, &c->memory_max, message, flags, error);
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->cpu_shares = shares;
- unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
+ if (streq(name, "MemoryLimit"))
+ return bus_cgroup_set_memory(u, name, &c->memory_limit, message, flags, error);
- if (shares == CGROUP_CPU_SHARES_INVALID)
- unit_write_setting(u, flags, name, "CPUShares=");
- else
- unit_write_settingf(u, flags, name, "CPUShares=%" PRIu64, shares);
- }
+ if (streq(name, "MemoryLowScale"))
+ return bus_cgroup_set_memory_scale(u, name, &c->memory_low, message, flags, error);
- return 1;
+ if (streq(name, "MemoryHighScale"))
+ return bus_cgroup_set_memory_scale(u, name, &c->memory_high, message, flags, error);
- } else if (streq(name, "StartupCPUShares")) {
- uint64_t shares;
+ if (streq(name, "MemorySwapMaxScale"))
+ return bus_cgroup_set_memory_scale(u, name, &c->memory_swap_max, message, flags, error);
- r = sd_bus_message_read(message, "t", &shares);
- if (r < 0)
- return r;
+ if (streq(name, "MemoryMaxScale"))
+ return bus_cgroup_set_memory_scale(u, name, &c->memory_max, message, flags, error);
- if (!CGROUP_CPU_SHARES_IS_OK(shares))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "StartupCPUShares= value out of range");
+ if (streq(name, "MemoryLimitScale"))
+ return bus_cgroup_set_memory_scale(u, name, &c->memory_limit, message, flags, error);
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->startup_cpu_shares = shares;
- unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
+ if (streq(name, "TasksAccounting"))
+ return bus_cgroup_set_boolean(u, name, &c->tasks_accounting, CGROUP_MASK_PIDS, message, flags, error);
- if (shares == CGROUP_CPU_SHARES_INVALID)
- unit_write_setting(u, flags, name, "StartupCPUShares=");
- else
- unit_write_settingf(u, flags, name, "StartupCPUShares=%" PRIu64, shares);
- }
+ if (streq(name, "TasksMax"))
+ return bus_cgroup_set_tasks_max(u, name, &c->tasks_max, message, flags, error);
- return 1;
+ if (streq(name, "TasksMaxScale"))
+ return bus_cgroup_set_tasks_max_scale(u, name, &c->tasks_max, message, flags, error);
- } else if (streq(name, "CPUQuotaPerSecUSec")) {
+ if (streq(name, "CPUQuotaPerSecUSec")) {
uint64_t u64;
r = sd_bus_message_read(message, "t", &u64);
@@ -560,65 +568,6 @@ int bus_cgroup_set_property(
return 1;
- } else if (streq(name, "IOAccounting")) {
- int b;
-
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->io_accounting = b;
- unit_invalidate_cgroup(u, CGROUP_MASK_IO);
- unit_write_settingf(u, flags, name, "IOAccounting=%s", yes_no(b));
- }
-
- return 1;
-
- } else if (streq(name, "IOWeight")) {
- uint64_t weight;
-
- r = sd_bus_message_read(message, "t", &weight);
- if (r < 0)
- return r;
-
- if (!CGROUP_WEIGHT_IS_OK(weight))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "IOWeight= value out of range");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->io_weight = weight;
- unit_invalidate_cgroup(u, CGROUP_MASK_IO);
-
- if (weight == CGROUP_WEIGHT_INVALID)
- unit_write_setting(u, flags, name, "IOWeight=");
- else
- unit_write_settingf(u, flags, name, "IOWeight=%" PRIu64, weight);
- }
-
- return 1;
-
- } else if (streq(name, "StartupIOWeight")) {
- uint64_t weight;
-
- r = sd_bus_message_read(message, "t", &weight);
- if (r < 0)
- return r;
-
- if (CGROUP_WEIGHT_IS_OK(weight))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "StartupIOWeight= value out of range");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->startup_io_weight = weight;
- unit_invalidate_cgroup(u, CGROUP_MASK_IO);
-
- if (weight == CGROUP_WEIGHT_INVALID)
- unit_write_setting(u, flags, name, "StartupIOWeight=");
- else
- unit_write_settingf(u, flags, name, "StartupIOWeight=%" PRIu64, weight);
- }
-
- return 1;
-
} else if ((iol_type = cgroup_io_limit_type_from_string(name)) >= 0) {
const char *path;
unsigned n = 0;
@@ -630,6 +579,10 @@ int bus_cgroup_set_property(
while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
+ if (!path_startswith(path, "/dev") &&
+ !path_startswith(path, "/run/systemd/inaccessible/"))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s specified in %s= is not a device file in /dev", name, path);
+
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
CGroupIODeviceLimit *a = NULL, *b;
@@ -714,6 +667,10 @@ int bus_cgroup_set_property(
while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) {
+ if (!path_startswith(path, "/dev") &&
+ !path_startswith(path, "/run/systemd/inaccessible/"))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s specified in %s= is not a device file in /dev", name, path);
+
if (!CGROUP_WEIGHT_IS_OK(weight) || weight == CGROUP_WEIGHT_INVALID)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "IODeviceWeight= value out of range");
@@ -737,7 +694,7 @@ int bus_cgroup_set_property(
free(a);
return -ENOMEM;
}
- LIST_PREPEND(device_weights,c->io_device_weights, a);
+ LIST_PREPEND(device_weights, c->io_device_weights, a);
}
a->weight = weight;
@@ -781,65 +738,6 @@ int bus_cgroup_set_property(
return 1;
- } else if (streq(name, "BlockIOAccounting")) {
- int b;
-
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->blockio_accounting = b;
- unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
- unit_write_settingf(u, flags, name, "BlockIOAccounting=%s", yes_no(b));
- }
-
- return 1;
-
- } else if (streq(name, "BlockIOWeight")) {
- uint64_t weight;
-
- r = sd_bus_message_read(message, "t", &weight);
- if (r < 0)
- return r;
-
- if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "BlockIOWeight= value out of range");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->blockio_weight = weight;
- unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
-
- if (weight == CGROUP_BLKIO_WEIGHT_INVALID)
- unit_write_setting(u, flags, name, "BlockIOWeight=");
- else
- unit_write_settingf(u, flags, name, "BlockIOWeight=%" PRIu64, weight);
- }
-
- return 1;
-
- } else if (streq(name, "StartupBlockIOWeight")) {
- uint64_t weight;
-
- r = sd_bus_message_read(message, "t", &weight);
- if (r < 0)
- return r;
-
- if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "StartupBlockIOWeight= value out of range");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->startup_blockio_weight = weight;
- unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
-
- if (weight == CGROUP_BLKIO_WEIGHT_INVALID)
- unit_write_setting(u, flags, name, "StartupBlockIOWeight=");
- else
- unit_write_settingf(u, flags, name, "StartupBlockIOWeight=%" PRIu64, weight);
- }
-
- return 1;
-
} else if (STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
const char *path;
bool read = true;
@@ -855,6 +753,10 @@ int bus_cgroup_set_property(
while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
+ if (!path_startswith(path, "/dev") &&
+ !path_startswith(path, "/run/systemd/inaccessible/"))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s specified in %s= is not a device file in /dev", name, path);
+
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
CGroupBlockIODeviceBandwidth *a = NULL, *b;
@@ -951,6 +853,10 @@ int bus_cgroup_set_property(
while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) {
+ if (!path_startswith(path, "/dev") &&
+ !path_startswith(path, "/run/systemd/inaccessible/"))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s specified in %s= is not a device file in /dev", name, path);
+
if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight) || weight == CGROUP_BLKIO_WEIGHT_INVALID)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "BlockIODeviceWeight= out of range");
@@ -974,7 +880,7 @@ int bus_cgroup_set_property(
free(a);
return -ENOMEM;
}
- LIST_PREPEND(device_weights,c->blockio_device_weights, a);
+ LIST_PREPEND(device_weights, c->blockio_device_weights, a);
}
a->weight = weight;
@@ -1019,127 +925,6 @@ int bus_cgroup_set_property(
return 1;
- } else if (streq(name, "MemoryAccounting")) {
- int b;
-
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->memory_accounting = b;
- unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
- unit_write_settingf(u, flags, name, "MemoryAccounting=%s", yes_no(b));
- }
-
- return 1;
-
- } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax")) {
- uint64_t v;
-
- r = sd_bus_message_read(message, "t", &v);
- if (r < 0)
- return r;
- if (v <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is too small", name);
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- if (streq(name, "MemoryLow"))
- c->memory_low = v;
- else if (streq(name, "MemoryHigh"))
- c->memory_high = v;
- else if (streq(name, "MemorySwapMax"))
- c->memory_swap_max = v;
- else
- c->memory_max = v;
-
- unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
-
- if (v == CGROUP_LIMIT_MAX)
- unit_write_settingf(u, flags, name, "%s=infinity", name);
- else
- unit_write_settingf(u, flags, name, "%s=%" PRIu64, name, v);
- }
-
- return 1;
-
- } else if (STR_IN_SET(name, "MemoryLowScale", "MemoryHighScale", "MemoryMaxScale", "MemorySwapMaxScale")) {
- uint32_t raw;
- uint64_t v;
-
- r = sd_bus_message_read(message, "u", &raw);
- if (r < 0)
- return r;
-
- v = physical_memory_scale(raw, UINT32_MAX);
- if (v <= 0 || v == UINT64_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is out of range", name);
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- const char *e;
-
- /* Chop off suffix */
- assert_se(e = endswith(name, "Scale"));
- name = strndupa(name, e - name);
-
- if (streq(name, "MemoryLow"))
- c->memory_low = v;
- else if (streq(name, "MemoryHigh"))
- c->memory_high = v;
- else if (streq(name, "MemorySwapMaxScale"))
- c->memory_swap_max = v;
- else /* MemoryMax */
- c->memory_max = v;
-
- unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
- unit_write_settingf(u, flags, name, "%s=%" PRIu32 "%%", name,
- (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX)));
- }
-
- return 1;
-
- } else if (streq(name, "MemoryLimit")) {
- uint64_t limit;
-
- r = sd_bus_message_read(message, "t", &limit);
- if (r < 0)
- return r;
- if (limit <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is too small", name);
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->memory_limit = limit;
- unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
-
- if (limit == CGROUP_LIMIT_MAX)
- unit_write_setting(u, flags, name, "MemoryLimit=infinity");
- else
- unit_write_settingf(u, flags, name, "MemoryLimit=%" PRIu64, limit);
- }
-
- return 1;
-
- } else if (streq(name, "MemoryLimitScale")) {
- uint64_t limit;
- uint32_t raw;
-
- r = sd_bus_message_read(message, "u", &raw);
- if (r < 0)
- return r;
-
- limit = physical_memory_scale(raw, UINT32_MAX);
- if (limit <= 0 || limit == UINT64_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is out of range", name);
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->memory_limit = limit;
- unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
- unit_write_settingf(u, flags, "MemoryLimit", "MemoryLimit=%" PRIu32 "%%",
- (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX)));
- }
-
- return 1;
-
} else if (streq(name, "DevicePolicy")) {
const char *policy;
CGroupDevicePolicy p;
@@ -1170,10 +955,8 @@ int bus_cgroup_set_property(
while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
- if ((!path_startswith(path, "/dev/") &&
- !path_startswith(path, "/run/systemd/inaccessible/") &&
- !startswith(path, "block-") &&
- !startswith(path, "char-")) ||
+ if ((!is_deviceallow_pattern(path) &&
+ !path_startswith(path, "/run/systemd/inaccessible/")) ||
strpbrk(path, WHITESPACE))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node");
@@ -1252,63 +1035,6 @@ int bus_cgroup_set_property(
return 1;
- } else if (streq(name, "TasksAccounting")) {
- int b;
-
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->tasks_accounting = b;
- unit_invalidate_cgroup(u, CGROUP_MASK_PIDS);
- unit_write_settingf(u, flags, name, "TasksAccounting=%s", yes_no(b));
- }
-
- return 1;
-
- } else if (streq(name, "TasksMax")) {
- uint64_t limit;
-
- r = sd_bus_message_read(message, "t", &limit);
- if (r < 0)
- return r;
- if (limit <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is too small", name);
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->tasks_max = limit;
- unit_invalidate_cgroup(u, CGROUP_MASK_PIDS);
-
- if (limit == (uint64_t) -1)
- unit_write_setting(u, flags, name, "TasksMax=infinity");
- else
- unit_write_settingf(u, flags, name, "TasksMax=%" PRIu64, limit);
- }
-
- return 1;
-
- } else if (streq(name, "TasksMaxScale")) {
- uint64_t limit;
- uint32_t raw;
-
- r = sd_bus_message_read(message, "u", &raw);
- if (r < 0)
- return r;
-
- limit = system_tasks_max_scale(raw, UINT32_MAX);
- if (limit <= 0 || limit >= UINT64_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is out of range", name);
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->tasks_max = limit;
- unit_invalidate_cgroup(u, CGROUP_MASK_PIDS);
- unit_write_settingf(u, flags, name, "TasksMax=%" PRIu32 "%%",
- (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX)));
- }
-
- return 1;
-
} else if (streq(name, "IPAccounting")) {
int b;
@@ -1450,12 +1176,8 @@ int bus_cgroup_set_property(
return 1;
}
- if (u->transient && u->load_state == UNIT_STUB) {
- r = bus_cgroup_set_transient_property(u, c, name, message, flags, error);
- if (r != 0)
- return r;
-
- }
+ if (u->transient && u->load_state == UNIT_STUB)
+ return bus_cgroup_set_transient_property(u, c, name, message, flags, error);
return 0;
}
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index be25b6e987..628fdcd1e5 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -32,6 +32,7 @@
#include "capability-util.h"
#include "cpu-set-util.h"
#include "dbus-execute.h"
+#include "dbus-util.h"
#include "env-util.h"
#include "errno-list.h"
#include "escape.h"
@@ -1036,6 +1037,162 @@ int bus_property_get_exec_command_list(
return sd_bus_message_close_container(reply);
}
+int bus_set_transient_exec_command(
+ Unit *u,
+ const char *name,
+ ExecCommand **exec_command,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
+ unsigned n = 0;
+ int r;
+
+ r = sd_bus_message_enter_container(message, 'a', "(sasb)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_enter_container(message, 'r', "sasb")) > 0) {
+ _cleanup_strv_free_ char **argv = NULL;
+ const char *path;
+ int b;
+
+ r = sd_bus_message_read(message, "s", &path);
+ if (r < 0)
+ return r;
+
+ if (!path_is_absolute(path))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path);
+
+ r = sd_bus_message_read_strv(message, &argv);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ ExecCommand *c;
+
+ c = new0(ExecCommand, 1);
+ if (!c)
+ return -ENOMEM;
+
+ c->path = strdup(path);
+ if (!c->path) {
+ free(c);
+ return -ENOMEM;
+ }
+
+ c->argv = argv;
+ argv = NULL;
+
+ c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0;
+
+ path_kill_slashes(c->path);
+ exec_command_append_list(exec_command, c);
+ }
+
+ n++;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ ExecCommand *c;
+ size_t size = 0;
+
+ if (n == 0)
+ *exec_command = exec_command_free_list(*exec_command);
+
+ f = open_memstream(&buf, &size);
+ if (!f)
+ return -ENOMEM;
+
+ (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+
+ fputs("ExecStart=\n", f);
+
+ LIST_FOREACH(command, c, *exec_command) {
+ _cleanup_free_ char *a = NULL, *t = NULL;
+ const char *p;
+
+ p = unit_escape_setting(c->path, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS, &t);
+ if (!p)
+ return -ENOMEM;
+
+ a = unit_concat_strv(c->argv, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS);
+ if (!a)
+ return -ENOMEM;
+
+ fprintf(f, "%s=%s@%s %s\n",
+ name,
+ c->flags & EXEC_COMMAND_IGNORE_FAILURE ? "-" : "",
+ p,
+ a);
+ }
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
+
+ unit_write_setting(u, flags, name, buf);
+ }
+
+ return 1;
+}
+
+static int parse_personality(const char *s, unsigned long *p) {
+ unsigned long v;
+
+ assert(p);
+
+ v = personality_from_string(s);
+ if (v == PERSONALITY_INVALID)
+ return -EINVAL;
+
+ *p = v;
+ return 0;
+}
+
+static const char* mount_propagation_flags_to_string_with_check(unsigned long n) {
+ if (!IN_SET(n, 0, MS_SHARED, MS_PRIVATE, MS_SLAVE))
+ return NULL;
+
+ return mount_propagation_flags_to_string(n);
+}
+
+static BUS_DEFINE_SET_TRANSIENT(nsec, "t", uint64_t, nsec_t, NSEC_FMT);
+static BUS_DEFINE_SET_TRANSIENT_IS_VALID(log_level, "i", int32_t, int, "%" PRIi32, log_level_is_valid);
+#if HAVE_SECCOMP
+static BUS_DEFINE_SET_TRANSIENT_IS_VALID(errno, "i", int32_t, int, "%" PRIi32, errno_is_valid);
+#endif
+static BUS_DEFINE_SET_TRANSIENT_IS_VALID(sched_priority, "i", int32_t, int, "%" PRIi32, sched_priority_is_valid);
+static BUS_DEFINE_SET_TRANSIENT_IS_VALID(nice, "i", int32_t, int, "%" PRIi32, nice_is_valid);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(std_input, ExecInput, exec_input_from_string);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(std_output, ExecOutput, exec_output_from_string);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(utmp_mode, ExecUtmpMode, exec_utmp_mode_from_string);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_system, ProtectSystem, parse_protect_system_or_bool);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_home, ProtectHome, parse_protect_home_or_bool);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(keyring_mode, ExecKeyringMode, exec_keyring_mode_from_string);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(preserve_mode, ExecPreserveMode, exec_preserve_mode_from_string);
+static BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(personality, unsigned long, parse_personality);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(secure_bits, "i", int32_t, int, "%" PRIi32, secure_bits_to_string_alloc_with_check);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(capability, "t", uint64_t, uint64_t, "%" PRIu64, capability_set_to_string_alloc);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(sched_policy, "i", int32_t, int, "%" PRIi32, sched_policy_to_string_alloc_with_check);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(namespace_flag, "t", uint64_t, unsigned long, "%" PRIu64, namespace_flag_to_string_many_with_check);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING(mount_flags, "t", uint64_t, unsigned long, "%" PRIu64, mount_propagation_flags_to_string_with_check);
+
int bus_exec_context_set_transient_property(
Unit *u,
ExecContext *c,
@@ -1054,49 +1211,172 @@ int bus_exec_context_set_transient_property(
flags |= UNIT_PRIVATE;
- if (streq(name, "User")) {
- const char *uu;
+ if (streq(name, "User"))
+ return bus_set_transient_user(u, name, &c->user, message, flags, error);
- r = sd_bus_message_read(message, "s", &uu);
- if (r < 0)
- return r;
+ if (streq(name, "Group"))
+ return bus_set_transient_user(u, name, &c->group, message, flags, error);
- if (!isempty(uu) && !valid_user_group_name_or_id(uu))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user name: %s", uu);
+ if (streq(name, "TTYPath"))
+ return bus_set_transient_path(u, name, &c->tty_path, message, flags, error);
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ if (streq(name, "RootImage"))
+ return bus_set_transient_path(u, name, &c->root_image, message, flags, error);
- r = free_and_strdup(&c->user, empty_to_null(uu));
- if (r < 0)
- return r;
+ if (streq(name, "RootDirectory"))
+ return bus_set_transient_path(u, name, &c->root_directory, message, flags, error);
- unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "User=%s", uu);
- }
+ if (streq(name, "SyslogIdentifier"))
+ return bus_set_transient_string(u, name, &c->syslog_identifier, message, flags, error);
- return 1;
+ if (streq(name, "LogLevelMax"))
+ return bus_set_transient_log_level(u, name, &c->log_level_max, message, flags, error);
- } else if (streq(name, "Group")) {
- const char *gg;
+ if (streq(name, "CPUSchedulingPriority"))
+ return bus_set_transient_sched_priority(u, name, &c->cpu_sched_priority, message, flags, error);
- r = sd_bus_message_read(message, "s", &gg);
- if (r < 0)
- return r;
+ if (streq(name, "Personality"))
+ return bus_set_transient_personality(u, name, &c->personality, message, flags, error);
- if (!isempty(gg) && !valid_user_group_name_or_id(gg))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group name: %s", gg);
+ if (streq(name, "Nice"))
+ return bus_set_transient_nice(u, name, &c->nice, message, flags, error);
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ if (streq(name, "StandardInput"))
+ return bus_set_transient_std_input(u, name, &c->std_input, message, flags, error);
- r = free_and_strdup(&c->group, empty_to_null(gg));
- if (r < 0)
- return r;
+ if (streq(name, "StandardOutput"))
+ return bus_set_transient_std_output(u, name, &c->std_output, message, flags, error);
- unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "Group=%s", gg);
- }
+ if (streq(name, "StandardError"))
+ return bus_set_transient_std_output(u, name, &c->std_error, message, flags, error);
- return 1;
+ if (streq(name, "IgnoreSIGPIPE"))
+ return bus_set_transient_bool(u, name, &c->ignore_sigpipe, message, flags, error);
+
+ if (streq(name, "TTYVHangup"))
+ return bus_set_transient_bool(u, name, &c->tty_vhangup, message, flags, error);
+
+ if (streq(name, "TTYReset"))
+ return bus_set_transient_bool(u, name, &c->tty_reset, message, flags, error);
+
+ if (streq(name, "TTYVTDisallocate"))
+ return bus_set_transient_bool(u, name, &c->tty_vt_disallocate, message, flags, error);
+
+ if (streq(name, "PrivateTmp"))
+ return bus_set_transient_bool(u, name, &c->private_tmp, message, flags, error);
+
+ if (streq(name, "PrivateDevices"))
+ return bus_set_transient_bool(u, name, &c->private_devices, message, flags, error);
+
+ if (streq(name, "PrivateNetwork"))
+ return bus_set_transient_bool(u, name, &c->private_network, message, flags, error);
+
+ if (streq(name, "PrivateUsers"))
+ return bus_set_transient_bool(u, name, &c->private_users, message, flags, error);
+
+ if (streq(name, "NoNewPrivileges"))
+ return bus_set_transient_bool(u, name, &c->no_new_privileges, message, flags, error);
+
+ if (streq(name, "SyslogLevelPrefix"))
+ return bus_set_transient_bool(u, name, &c->syslog_level_prefix, message, flags, error);
+
+ if (streq(name, "MemoryDenyWriteExecute"))
+ return bus_set_transient_bool(u, name, &c->memory_deny_write_execute, message, flags, error);
+
+ if (streq(name, "RestrictRealtime"))
+ return bus_set_transient_bool(u, name, &c->restrict_realtime, message, flags, error);
+
+ if (streq(name, "DynamicUser"))
+ return bus_set_transient_bool(u, name, &c->dynamic_user, message, flags, error);
+
+ if (streq(name, "RemoveIPC"))
+ return bus_set_transient_bool(u, name, &c->remove_ipc, message, flags, error);
+
+ if (streq(name, "ProtectKernelTunables"))
+ return bus_set_transient_bool(u, name, &c->protect_kernel_tunables, message, flags, error);
+
+ if (streq(name, "ProtectKernelModules"))
+ return bus_set_transient_bool(u, name, &c->protect_kernel_modules, message, flags, error);
+
+ if (streq(name, "ProtectControlGroups"))
+ return bus_set_transient_bool(u, name, &c->protect_control_groups, message, flags, error);
+
+ if (streq(name, "MountAPIVFS"))
+ return bus_set_transient_bool(u, name, &c->mount_apivfs, message, flags, error);
+
+ if (streq(name, "CPUSchedulingResetOnFork"))
+ return bus_set_transient_bool(u, name, &c->cpu_sched_reset_on_fork, message, flags, error);
+
+ if (streq(name, "NonBlocking"))
+ return bus_set_transient_bool(u, name, &c->non_blocking, message, flags, error);
+
+ if (streq(name, "LockPersonality"))
+ return bus_set_transient_bool(u, name, &c->lock_personality, message, flags, error);
+
+ if (streq(name, "UtmpIdentifier"))
+ return bus_set_transient_string(u, name, &c->utmp_id, message, flags, error);
+
+ if (streq(name, "UtmpMode"))
+ return bus_set_transient_utmp_mode(u, name, &c->utmp_mode, message, flags, error);
+
+ if (streq(name, "PAMName"))
+ return bus_set_transient_string(u, name, &c->pam_name, message, flags, error);
+
+ if (streq(name, "TimerSlackNSec"))
+ return bus_set_transient_nsec(u, name, &c->timer_slack_nsec, message, flags, error);
+
+ if (streq(name, "ProtectSystem"))
+ return bus_set_transient_protect_system(u, name, &c->protect_system, message, flags, error);
+
+ if (streq(name, "ProtectHome"))
+ return bus_set_transient_protect_home(u, name, &c->protect_home, message, flags, error);
+
+ if (streq(name, "KeyringMode"))
+ return bus_set_transient_keyring_mode(u, name, &c->keyring_mode, message, flags, error);
+
+ if (streq(name, "RuntimeDirectoryPreserve"))
+ return bus_set_transient_preserve_mode(u, name, &c->runtime_directory_preserve_mode, message, flags, error);
+
+ if (streq(name, "UMask"))
+ return bus_set_transient_mode_t(u, name, &c->umask, message, flags, error);
+
+ if (streq(name, "RuntimeDirectoryMode"))
+ return bus_set_transient_mode_t(u, name, &c->directories[EXEC_DIRECTORY_RUNTIME].mode, message, flags, error);
- } else if (streq(name, "SupplementaryGroups")) {
+ if (streq(name, "StateDirectoryMode"))
+ return bus_set_transient_mode_t(u, name, &c->directories[EXEC_DIRECTORY_STATE].mode, message, flags, error);
+
+ if (streq(name, "CacheDirectoryMode"))
+ return bus_set_transient_mode_t(u, name, &c->directories[EXEC_DIRECTORY_CACHE].mode, message, flags, error);
+
+ if (streq(name, "LogsDirectoryMode"))
+ return bus_set_transient_mode_t(u, name, &c->directories[EXEC_DIRECTORY_LOGS].mode, message, flags, error);
+
+ if (streq(name, "ConfigurationDirectoryMode"))
+ return bus_set_transient_mode_t(u, name, &c->directories[EXEC_DIRECTORY_CONFIGURATION].mode, message, flags, error);
+
+ if (streq(name, "SELinuxContext"))
+ return bus_set_transient_string(u, name, &c->selinux_context, message, flags, error);
+
+ if (streq(name, "SecureBits"))
+ return bus_set_transient_secure_bits(u, name, &c->secure_bits, message, flags, error);
+
+ if (streq(name, "CapabilityBoundingSet"))
+ return bus_set_transient_capability(u, name, &c->capability_bounding_set, message, flags, error);
+
+ if (streq(name, "AmbientCapabilities"))
+ return bus_set_transient_capability(u, name, &c->capability_ambient_set, message, flags, error);
+
+ if (streq(name, "CPUSchedulingPolicy"))
+ return bus_set_transient_sched_policy(u, name, &c->cpu_sched_policy, message, flags, error);
+
+ if (streq(name, "RestrictNamespaces"))
+ return bus_set_transient_namespace_flag(u, name, &c->restrict_namespaces, message, flags, error);
+
+ if (streq(name, "MountFlags"))
+ return bus_set_transient_mount_flags(u, name, &c->mount_flags, message, flags, error);
+
+ if (streq(name, "SupplementaryGroups")) {
_cleanup_strv_free_ char **l = NULL;
char **p;
@@ -1130,24 +1410,6 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (streq(name, "SyslogIdentifier")) {
- const char *id;
-
- r = sd_bus_message_read(message, "s", &id);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-
- if (isempty(id))
- c->syslog_identifier = mfree(c->syslog_identifier);
- else if (free_and_strdup(&c->syslog_identifier, id) < 0)
- return -ENOMEM;
-
- unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "SyslogIdentifier=%s", id);
- }
-
- return 1;
} else if (streq(name, "SyslogLevel")) {
int32_t level;
@@ -1164,6 +1426,7 @@ int bus_exec_context_set_transient_property(
}
return 1;
+
} else if (streq(name, "SyslogFacility")) {
int32_t facility;
@@ -1181,23 +1444,6 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (streq(name, "LogLevelMax")) {
- int32_t level;
-
- r = sd_bus_message_read(message, "i", &level);
- if (r < 0)
- return r;
-
- if (!log_level_is_valid(level))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Maximum log level value out of range");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->log_level_max = level;
- unit_write_settingf(u, flags, name, "LogLevelMax=%i", level);
- }
-
- return 1;
-
} else if (streq(name, "LogExtraFields")) {
size_t n = 0;
@@ -1271,75 +1517,14 @@ int bus_exec_context_set_transient_property(
}
return 1;
-
- } else if (streq(name, "SecureBits")) {
- int n;
-
- r = sd_bus_message_read(message, "i", &n);
- if (r < 0)
- return r;
-
- if (!secure_bits_is_valid(n))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid secure bits");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- _cleanup_free_ char *str = NULL;
-
- c->secure_bits = n;
- r = secure_bits_to_string_alloc(n, &str);
- if (r < 0)
- return r;
-
- unit_write_settingf(u, flags, name, "SecureBits=%s", str);
- }
-
- return 1;
- } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) {
- uint64_t n;
-
- r = sd_bus_message_read(message, "t", &n);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- _cleanup_free_ char *str = NULL;
-
- if (streq(name, "CapabilityBoundingSet"))
- c->capability_bounding_set = n;
- else /* "AmbientCapabilities" */
- c->capability_ambient_set = n;
-
- r = capability_set_to_string_alloc(n, &str);
- if (r < 0)
- return r;
-
- unit_write_settingf(u, flags, name, "%s=%s", name, str);
- }
-
- return 1;
-
- } else if (streq(name, "Personality")) {
- const char *s;
- unsigned long p;
-
- r = sd_bus_message_read(message, "s", &s);
- if (r < 0)
- return r;
-
- p = personality_from_string(s);
- if (p == PERSONALITY_INVALID)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid personality");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->personality = p;
- unit_write_settingf(u, flags, name, "%s=%s", name, s);
- }
-
- return 1;
+ }
#if HAVE_SECCOMP
- } else if (streq(name, "SystemCallFilter")) {
+ if (streq(name, "SystemCallErrorNumber"))
+ return bus_set_transient_errno(u, name, &c->syscall_errno, message, flags, error);
+
+ if (streq(name, "SystemCallFilter")) {
int whitelist;
_cleanup_strv_free_ char **l = NULL;
@@ -1361,59 +1546,42 @@ int bus_exec_context_set_transient_property(
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
_cleanup_free_ char *joined = NULL;
+ bool invert = !whitelist;
+ char **s;
if (strv_isempty(l)) {
c->syscall_whitelist = false;
c->syscall_filter = hashmap_free(c->syscall_filter);
- } else {
- char **s;
- c->syscall_whitelist = whitelist;
+ unit_write_settingf(u, flags, name, "SystemCallFilter=");
+ return 1;
+ }
- r = hashmap_ensure_allocated(&c->syscall_filter, NULL);
- if (r < 0)
- return r;
+ if (!c->syscall_filter) {
+ c->syscall_filter = hashmap_new(NULL);
+ if (!c->syscall_filter)
+ return log_oom();
- STRV_FOREACH(s, l) {
- _cleanup_free_ char *n = NULL;
- int e;
+ c->syscall_whitelist = whitelist;
- r = parse_syscall_and_errno(*s, &n, &e);
+ if (c->syscall_whitelist) {
+ r = seccomp_parse_syscall_filter(invert, "@default", -1, c->syscall_filter, true);
if (r < 0)
return r;
+ }
+ }
- if (*n == '@') {
- const SyscallFilterSet *set;
- const char *i;
-
- set = syscall_filter_set_find(n);
- if (!set)
- return -EINVAL;
-
- NULSTR_FOREACH(i, set->value) {
- int id;
-
- id = seccomp_syscall_resolve_name(i);
- if (id == __NR_SCMP_ERROR)
- return -EINVAL;
-
- r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(e));
- if (r < 0)
- return r;
- }
-
- } else {
- int id;
+ STRV_FOREACH(s, l) {
+ _cleanup_free_ char *n = NULL;
+ int e;
- id = seccomp_syscall_resolve_name(n);
- if (id == __NR_SCMP_ERROR)
- return -EINVAL;
+ r = parse_syscall_and_errno(*s, &n, &e);
+ if (r < 0)
+ return r;
- r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(e));
- if (r < 0)
- return r;
- }
- }
+ r = seccomp_parse_syscall_filter(invert, n, e, c->syscall_filter, c->syscall_whitelist);
+ if (r < 0)
+ return r;
}
joined = strv_join(l, " ");
@@ -1467,24 +1635,6 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (streq(name, "SystemCallErrorNumber")) {
- int32_t n;
-
- r = sd_bus_message_read(message, "i", &n);
- if (r < 0)
- return r;
-
- if (n <= 0 || n > ERRNO_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid SystemCallErrorNumber");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->syscall_errno = n;
-
- unit_write_settingf(u, flags, name, "SystemCallErrorNumber=%d", n);
- }
-
- return 1;
-
} else if (streq(name, "RestrictAddressFamilies")) {
int whitelist;
_cleanup_strv_free_ char **l = NULL;
@@ -1507,30 +1657,38 @@ int bus_exec_context_set_transient_property(
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
_cleanup_free_ char *joined = NULL;
+ bool invert = !whitelist;
+ char **s;
if (strv_isempty(l)) {
c->address_families_whitelist = false;
c->address_families = set_free(c->address_families);
- } else {
- char **s;
- c->address_families_whitelist = whitelist;
+ unit_write_settingf(u, flags, name, "RestrictAddressFamilies=");
+ return 1;
+ }
- r = set_ensure_allocated(&c->address_families, NULL);
- if (r < 0)
- return r;
+ if (!c->address_families) {
+ c->address_families = set_new(NULL);
+ if (!c->address_families)
+ return log_oom();
- STRV_FOREACH(s, l) {
- int af;
+ c->address_families_whitelist = whitelist;
+ }
- af = af_from_name(*s);
- if (af <= 0)
- return -EINVAL;
+ STRV_FOREACH(s, l) {
+ int af;
+ af = af_from_name(*s);
+ if (af <= 0)
+ return -EINVAL;
+
+ if (!invert == c->address_families_whitelist) {
r = set_put(c->address_families, INT_TO_PTR(af));
if (r < 0)
return r;
- }
+ } else
+ (void) set_remove(c->address_families, INT_TO_PTR(af));
}
joined = strv_join(l, " ");
@@ -1541,49 +1699,9 @@ int bus_exec_context_set_transient_property(
}
return 1;
+ }
#endif
-
- } else if (streq(name, "CPUSchedulingPolicy")) {
- int32_t n;
-
- r = sd_bus_message_read(message, "i", &n);
- if (r < 0)
- return r;
-
- if (!sched_policy_is_valid(n))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid CPU scheduling policy");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- _cleanup_free_ char *str = NULL;
-
- c->cpu_sched_policy = n;
- r = sched_policy_to_string_alloc(n, &str);
- if (r < 0)
- return r;
-
- unit_write_settingf(u, flags, name, "CPUSchedulingPolicy=%s", str);
- }
-
- return 1;
-
- } else if (streq(name, "CPUSchedulingPriority")) {
- int32_t n;
-
- r = sd_bus_message_read(message, "i", &n);
- if (r < 0)
- return r;
-
- if (!ioprio_priority_is_valid(n))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid CPU scheduling priority");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->cpu_sched_priority = n;
- unit_write_settingf(u, flags, name, "CPUSchedulingPriority=%i", n);
- }
-
- return 1;
-
- } else if (streq(name, "CPUAffinity")) {
+ if (streq(name, "CPUAffinity")) {
const void *a;
size_t n = 0;
@@ -1649,22 +1767,6 @@ int bus_exec_context_set_transient_property(
}
return 1;
- } else if (streq(name, "Nice")) {
- int32_t n;
-
- r = sd_bus_message_read(message, "i", &n);
- if (r < 0)
- return r;
-
- if (!nice_is_valid(n))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Nice value out of range");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->nice = n;
- unit_write_settingf(u, flags, name, "Nice=%i", n);
- }
-
- return 1;
} else if (streq(name, "IOSchedulingClass")) {
int32_t q;
@@ -1710,33 +1812,6 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (STR_IN_SET(name, "TTYPath", "RootDirectory", "RootImage")) {
- const char *s;
-
- r = sd_bus_message_read(message, "s", &s);
- if (r < 0)
- return r;
-
- if (!path_is_absolute(s))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s takes an absolute path", name);
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- if (streq(name, "TTYPath"))
- r = free_and_strdup(&c->tty_path, s);
- else if (streq(name, "RootImage"))
- r = free_and_strdup(&c->root_image, s);
- else {
- assert(streq(name, "RootDirectory"));
- r = free_and_strdup(&c->root_directory, s);
- }
- if (r < 0)
- return r;
-
- unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, s);
- }
-
- return 1;
-
} else if (streq(name, "WorkingDirectory")) {
const char *s;
bool missing_ok;
@@ -1751,7 +1826,7 @@ int bus_exec_context_set_transient_property(
} else
missing_ok = false;
- if (!streq(s, "~") && !path_is_absolute(s))
+ if (!isempty(s) && !streq(s, "~") && !path_is_absolute(s))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "WorkingDirectory= expects an absolute path or '~'");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
@@ -1759,7 +1834,7 @@ int bus_exec_context_set_transient_property(
c->working_directory = mfree(c->working_directory);
c->working_directory_home = true;
} else {
- r = free_and_strdup(&c->working_directory, s);
+ r = free_and_strdup(&c->working_directory, empty_to_null(s));
if (r < 0)
return r;
@@ -1772,66 +1847,6 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (streq(name, "StandardInput")) {
- const char *s;
- ExecInput p;
-
- r = sd_bus_message_read(message, "s", &s);
- if (r < 0)
- return r;
-
- p = exec_input_from_string(s);
- if (p < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard input name");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->std_input = p;
-
- unit_write_settingf(u, flags, name, "StandardInput=%s", exec_input_to_string(p));
- }
-
- return 1;
-
- } else if (streq(name, "StandardOutput")) {
- const char *s;
- ExecOutput p;
-
- r = sd_bus_message_read(message, "s", &s);
- if (r < 0)
- return r;
-
- p = exec_output_from_string(s);
- if (p < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard output name");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->std_output = p;
-
- unit_write_settingf(u, flags, name, "StandardOutput=%s", exec_output_to_string(p));
- }
-
- return 1;
-
- } else if (streq(name, "StandardError")) {
- const char *s;
- ExecOutput p;
-
- r = sd_bus_message_read(message, "s", &s);
- if (r < 0)
- return r;
-
- p = exec_output_from_string(s);
- if (p < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard error name");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->std_error = p;
-
- unit_write_settingf(u, flags, name, "StandardError=%s", exec_output_to_string(p));
- }
-
- return 1;
-
} else if (STR_IN_SET(name,
"StandardInputFileDescriptorName", "StandardOutputFileDescriptorName", "StandardErrorFileDescriptorName")) {
const char *s;
@@ -1840,15 +1855,13 @@ int bus_exec_context_set_transient_property(
if (r < 0)
return r;
- if (isempty(s))
- s = NULL;
- else if (!fdname_is_valid(s))
+ if (!isempty(s) && !fdname_is_valid(s))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid file descriptor name");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (streq(name, "StandardInputFileDescriptorName")) {
- r = free_and_strdup(c->stdio_fdname + STDIN_FILENO, s);
+ r = free_and_strdup(c->stdio_fdname + STDIN_FILENO, empty_to_null(s));
if (r < 0)
return r;
@@ -1856,7 +1869,7 @@ int bus_exec_context_set_transient_property(
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardInput=fd:%s", exec_context_fdname(c, STDIN_FILENO));
} else if (streq(name, "StandardOutputFileDescriptorName")) {
- r = free_and_strdup(c->stdio_fdname + STDOUT_FILENO, s);
+ r = free_and_strdup(c->stdio_fdname + STDOUT_FILENO, empty_to_null(s));
if (r < 0)
return r;
@@ -1866,7 +1879,7 @@ int bus_exec_context_set_transient_property(
} else {
assert(streq(name, "StandardErrorFileDescriptorName"));
- r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], s);
+ r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], empty_to_null(s));
if (r < 0)
return r;
@@ -1884,15 +1897,17 @@ int bus_exec_context_set_transient_property(
if (r < 0)
return r;
- if (!path_is_absolute(s))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute", s);
- if (!path_is_normalized(s))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not normalized", s);
+ if (!isempty(s)) {
+ if (!path_is_absolute(s))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute", s);
+ if (!path_is_normalized(s))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not normalized", s);
+ }
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (streq(name, "StandardInputFile")) {
- r = free_and_strdup(&c->stdio_file[STDIN_FILENO], s);
+ r = free_and_strdup(&c->stdio_file[STDIN_FILENO], empty_to_null(s));
if (r < 0)
return r;
@@ -1900,7 +1915,7 @@ int bus_exec_context_set_transient_property(
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardInput=file:%s", s);
} else if (streq(name, "StandardOutputFile")) {
- r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], s);
+ r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], empty_to_null(s));
if (r < 0)
return r;
@@ -1910,7 +1925,7 @@ int bus_exec_context_set_transient_property(
} else {
assert(streq(name, "StandardErrorFile"));
- r = free_and_strdup(&c->stdio_file[STDERR_FILENO], s);
+ r = free_and_strdup(&c->stdio_file[STDERR_FILENO], empty_to_null(s));
if (r < 0)
return r;
@@ -1964,124 +1979,6 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (STR_IN_SET(name,
- "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
- "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
- "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute",
- "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables",
- "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
- "CPUSchedulingResetOnFork", "NonBlocking", "LockPersonality")) {
- int b;
-
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- if (streq(name, "IgnoreSIGPIPE"))
- c->ignore_sigpipe = b;
- else if (streq(name, "TTYVHangup"))
- c->tty_vhangup = b;
- else if (streq(name, "TTYReset"))
- c->tty_reset = b;
- else if (streq(name, "TTYVTDisallocate"))
- c->tty_vt_disallocate = b;
- else if (streq(name, "PrivateTmp"))
- c->private_tmp = b;
- else if (streq(name, "PrivateDevices"))
- c->private_devices = b;
- else if (streq(name, "PrivateNetwork"))
- c->private_network = b;
- else if (streq(name, "PrivateUsers"))
- c->private_users = b;
- else if (streq(name, "NoNewPrivileges"))
- c->no_new_privileges = b;
- else if (streq(name, "SyslogLevelPrefix"))
- c->syslog_level_prefix = b;
- else if (streq(name, "MemoryDenyWriteExecute"))
- c->memory_deny_write_execute = b;
- else if (streq(name, "RestrictRealtime"))
- c->restrict_realtime = b;
- else if (streq(name, "DynamicUser"))
- c->dynamic_user = b;
- else if (streq(name, "RemoveIPC"))
- c->remove_ipc = b;
- else if (streq(name, "ProtectKernelTunables"))
- c->protect_kernel_tunables = b;
- else if (streq(name, "ProtectKernelModules"))
- c->protect_kernel_modules = b;
- else if (streq(name, "ProtectControlGroups"))
- c->protect_control_groups = b;
- else if (streq(name, "MountAPIVFS"))
- c->mount_apivfs = b;
- else if (streq(name, "CPUSchedulingResetOnFork"))
- c->cpu_sched_reset_on_fork = b;
- else if (streq(name, "NonBlocking"))
- c->non_blocking = b;
- else if (streq(name, "LockPersonality"))
- c->lock_personality = b;
-
- unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(b));
- }
-
- return 1;
-
- } else if (streq(name, "UtmpIdentifier")) {
- const char *id;
-
- r = sd_bus_message_read(message, "s", &id);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-
- r = free_and_strdup(&c->utmp_id, empty_to_null(id));
- if (r < 0)
- return r;
-
- unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "UtmpIdentifier=%s", strempty(id));
- }
-
- return 1;
-
- } else if (streq(name, "UtmpMode")) {
- const char *s;
- ExecUtmpMode m;
-
- r = sd_bus_message_read(message, "s", &s);
- if (r < 0)
- return r;
-
- m = exec_utmp_mode_from_string(s);
- if (m < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid utmp mode");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->utmp_mode = m;
-
- unit_write_settingf(u, flags, name, "UtmpMode=%s", exec_utmp_mode_to_string(m));
- }
-
- return 1;
-
- } else if (streq(name, "PAMName")) {
- const char *n;
-
- r = sd_bus_message_read(message, "s", &n);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-
- r = free_and_strdup(&c->pam_name, empty_to_null(n));
- if (r < 0)
- return r;
-
- unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "PAMName=%s", strempty(n));
- }
-
- return 1;
-
} else if (streq(name, "Environment")) {
_cleanup_strv_free_ char **l = NULL;
@@ -2154,21 +2051,6 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (streq(name, "TimerSlackNSec")) {
-
- nsec_t n;
-
- r = sd_bus_message_read(message, "t", &n);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->timer_slack_nsec = n;
- unit_write_settingf(u, flags, name, "TimerSlackNSec=" NSEC_FMT, n);
- }
-
- return 1;
-
} else if (streq(name, "OOMScoreAdjust")) {
int oa;
@@ -2323,16 +2205,15 @@ int bus_exec_context_set_transient_property(
return r;
STRV_FOREACH(p, l) {
- const char *i = *p;
+ char *i = *p;
size_t offset;
- if (!utf8_is_valid(i))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
-
offset = i[0] == '-';
offset += i[offset] == '+';
if (!path_is_absolute(i + offset))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
+
+ path_kill_slashes(i + offset);
}
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
@@ -2363,123 +2244,6 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (streq(name, "ProtectSystem")) {
- const char *s;
- ProtectSystem ps;
-
- r = sd_bus_message_read(message, "s", &s);
- if (r < 0)
- return r;
-
- r = parse_boolean(s);
- if (r > 0)
- ps = PROTECT_SYSTEM_YES;
- else if (r == 0)
- ps = PROTECT_SYSTEM_NO;
- else {
- ps = protect_system_from_string(s);
- if (ps < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse protect system value");
- }
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->protect_system = ps;
- unit_write_settingf(u, flags, name, "%s=%s", name, s);
- }
-
- return 1;
-
- } else if (streq(name, "ProtectHome")) {
- const char *s;
- ProtectHome ph;
-
- r = sd_bus_message_read(message, "s", &s);
- if (r < 0)
- return r;
-
- r = parse_boolean(s);
- if (r > 0)
- ph = PROTECT_HOME_YES;
- else if (r == 0)
- ph = PROTECT_HOME_NO;
- else {
- ph = protect_home_from_string(s);
- if (ph < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse protect home value");
- }
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->protect_home = ph;
- unit_write_settingf(u, flags, name, "%s=%s", name, s);
- }
-
- return 1;
-
- } else if (streq(name, "KeyringMode")) {
-
- const char *s;
- ExecKeyringMode m;
-
- r = sd_bus_message_read(message, "s", &s);
- if (r < 0)
- return r;
-
- m = exec_keyring_mode_from_string(s);
- if (m < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid keyring mode");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->keyring_mode = m;
-
- unit_write_settingf(u, flags, name, "KeyringMode=%s", exec_keyring_mode_to_string(m));
- }
-
- return 1;
-
- } else if (streq(name, "RuntimeDirectoryPreserve")) {
- const char *s;
- ExecPreserveMode m;
-
- r = sd_bus_message_read(message, "s", &s);
- if (r < 0)
- return r;
-
- m = exec_preserve_mode_from_string(s);
- if (m < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid preserve mode");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->runtime_directory_preserve_mode = m;
-
- unit_write_settingf(u, flags, name, "RuntimeDirectoryPreserve=%s", exec_preserve_mode_to_string(m));
- }
-
- return 1;
-
- } else if (STR_IN_SET(name, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
- mode_t m;
-
- r = sd_bus_message_read(message, "u", &m);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- ExecDirectoryType i;
-
- if (streq(name, "UMask"))
- c->umask = m;
- else
- for (i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++)
- if (startswith(name, exec_directory_type_to_string(i))) {
- c->directories[i].mode = m;
- break;
- }
-
- unit_write_settingf(u, flags, name, "%s=%040o", name, m);
- }
-
- return 1;
-
} else if (STR_IN_SET(name, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
_cleanup_strv_free_ char **l = NULL;
char **p;
@@ -2525,23 +2289,6 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (streq(name, "SELinuxContext")) {
- const char *s;
- r = sd_bus_message_read(message, "s", &s);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- if (isempty(s))
- c->selinux_context = mfree(c->selinux_context);
- else if (free_and_strdup(&c->selinux_context, s) < 0)
- return -ENOMEM;
-
- unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, strempty(s));
- }
-
- return 1;
-
} else if (STR_IN_SET(name, "AppArmorProfile", "SmackProcessLabel")) {
int ignore;
const char *s;
@@ -2580,43 +2327,6 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (streq(name, "RestrictNamespaces")) {
- uint64_t rf;
-
- r = sd_bus_message_read(message, "t", &rf);
- if (r < 0)
- return r;
- if ((rf & NAMESPACE_FLAGS_ALL) != rf)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown namespace types");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- _cleanup_free_ char *s = NULL;
-
- r = namespace_flag_to_string_many(rf, &s);
- if (r < 0)
- return r;
-
- c->restrict_namespaces = rf;
- unit_write_settingf(u, flags, name, "%s=%s", name, s);
- }
-
- return 1;
- } else if (streq(name, "MountFlags")) {
- uint64_t fl;
-
- r = sd_bus_message_read(message, "t", &fl);
- if (r < 0)
- return r;
- if (!IN_SET(fl, 0, MS_SHARED, MS_PRIVATE, MS_SLAVE))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mount propagation flags");
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->mount_flags = fl;
-
- unit_write_settingf(u, flags, name, "%s=%s", name, mount_propagation_flags_to_string(fl));
- }
-
- return 1;
} else if (STR_IN_SET(name, "BindPaths", "BindReadOnlyPaths")) {
unsigned empty = true;
diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h
index f30f0774cd..4d9b368f81 100644
--- a/src/core/dbus-execute.h
+++ b/src/core/dbus-execute.h
@@ -44,3 +44,4 @@ int bus_property_get_exec_command(sd_bus *bus, const char *path, const char *int
int bus_property_get_exec_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
int bus_exec_context_set_transient_property(Unit *u, ExecContext *c, const char *name, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
+int bus_set_transient_exec_command(Unit *u, const char *name, ExecCommand **exec_command, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c
index bf3bbb2047..53d0aad63b 100644
--- a/src/core/dbus-kill.c
+++ b/src/core/dbus-kill.c
@@ -20,6 +20,7 @@
#include "bus-util.h"
#include "dbus-kill.h"
+#include "dbus-util.h"
#include "kill.h"
#include "signal-util.h"
@@ -34,6 +35,9 @@ const sd_bus_vtable bus_kill_vtable[] = {
SD_BUS_VTABLE_END
};
+static BUS_DEFINE_SET_TRANSIENT_PARSE(kill_mode, KillMode, kill_mode_from_string);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING(kill_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check);
+
int bus_kill_context_set_transient_property(
Unit *u,
KillContext *c,
@@ -42,8 +46,6 @@ int bus_kill_context_set_transient_property(
UnitWriteFlags flags,
sd_bus_error *error) {
- int r;
-
assert(u);
assert(c);
assert(name);
@@ -51,75 +53,17 @@ int bus_kill_context_set_transient_property(
flags |= UNIT_PRIVATE;
- if (streq(name, "KillMode")) {
- const char *m;
- KillMode k;
-
- r = sd_bus_message_read(message, "s", &m);
- if (r < 0)
- return r;
-
- k = kill_mode_from_string(m);
- if (k < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Kill mode '%s' not known.", m);
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->kill_mode = k;
-
- unit_write_settingf(u, flags, name, "KillMode=%s", kill_mode_to_string(k));
- }
-
- return 1;
-
- } else if (streq(name, "KillSignal")) {
- int sig;
-
- r = sd_bus_message_read(message, "i", &sig);
- if (r < 0)
- return r;
-
- if (!SIGNAL_VALID(sig))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal %i out of range", sig);
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->kill_signal = sig;
-
- unit_write_settingf(u, flags, name, "KillSignal=%s", signal_to_string(sig));
- }
-
- return 1;
-
- } else if (streq(name, "SendSIGHUP")) {
- int b;
-
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->send_sighup = b;
-
- unit_write_settingf(u, flags, name, "SendSIGHUP=%s", yes_no(b));
- }
-
- return 1;
-
- } else if (streq(name, "SendSIGKILL")) {
- int b;
-
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->send_sigkill = b;
+ if (streq(name, "KillMode"))
+ return bus_set_transient_kill_mode(u, name, &c->kill_mode, message, flags, error);
- unit_write_settingf(u, flags, name, "SendSIGKILL=%s", yes_no(b));
- }
+ if (streq(name, "SendSIGHUP"))
+ return bus_set_transient_bool(u, name, &c->send_sighup, message, flags, error);
- return 1;
+ if (streq(name, "SendSIGKILL"))
+ return bus_set_transient_bool(u, name, &c->send_sigkill, message, flags, error);
- }
+ if (streq(name, "KillSignal"))
+ return bus_set_transient_kill_signal(u, name, &c->kill_signal, message, flags, error);
return 0;
}
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index ec9e65879f..4fe374867c 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -2416,6 +2416,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0),
SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), 0),
+ SD_BUS_WRITABLE_PROPERTY("ServiceWatchdogs", "b", bus_property_get_bool, bus_property_set_bool, offsetof(Manager, service_watchdogs), 0),
SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0),
SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0),
SD_BUS_PROPERTY("ExitCode", "y", bus_property_get_unsigned, offsetof(Manager, return_value), 0),
@@ -2423,8 +2424,10 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("DefaultTimeoutStartUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultTimeoutStopUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultRestartUSec", "t", bus_property_get_usec, offsetof(Manager, default_restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("DefaultStartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */
+ SD_BUS_PROPERTY("DefaultStartLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ /* The following two items are obsolete alias */
+ SD_BUS_PROPERTY("DefaultStartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("DefaultStartLimitBurst", "u", bus_property_get_unsigned, offsetof(Manager, default_start_limit_burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultCPUAccounting", "b", bus_property_get_bool, offsetof(Manager, default_cpu_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultBlockIOAccounting", "b", bus_property_get_bool, offsetof(Manager, default_blockio_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index 628bce0b6a..9e52f55fa5 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -23,6 +23,7 @@
#include "dbus-execute.h"
#include "dbus-kill.h"
#include "dbus-mount.h"
+#include "dbus-util.h"
#include "mount.h"
#include "string-util.h"
#include "unit.h"
@@ -129,9 +130,7 @@ static int bus_mount_set_transient_property(
UnitWriteFlags flags,
sd_bus_error *error) {
- const char *new_property;
- char **property;
- int r;
+ Unit *u = UNIT(m);
assert(m);
assert(name);
@@ -139,29 +138,34 @@ static int bus_mount_set_transient_property(
flags |= UNIT_PRIVATE;
+ if (streq(name, "Where"))
+ return bus_set_transient_path(u, name, &m->where, message, flags, error);
+
if (streq(name, "What"))
- property = &m->parameters_fragment.what;
- else if (streq(name, "Options"))
- property = &m->parameters_fragment.options;
- else if (streq(name, "Type"))
- property = &m->parameters_fragment.fstype;
- else
- return 0;
-
- r = sd_bus_message_read(message, "s", &new_property);
- if (r < 0)
- return r;
+ return bus_set_transient_string(u, name, &m->parameters_fragment.what, message, flags, error);
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ if (streq(name, "Options"))
+ return bus_set_transient_string(u, name, &m->parameters_fragment.options, message, flags, error);
- r = free_and_strdup(property, new_property);
- if (r < 0)
- return r;
+ if (streq(name, "Type"))
+ return bus_set_transient_string(u, name, &m->parameters_fragment.fstype, message, flags, error);
- unit_write_settingf(UNIT(m), flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, new_property);
- }
+ if (streq(name, "TimeoutUSec"))
+ return bus_set_transient_usec_fix_0(u, name, &m->timeout_usec, message, flags, error);
- return 1;
+ if (streq(name, "DirectoryMode"))
+ return bus_set_transient_mode_t(u, name, &m->directory_mode, message, flags, error);
+
+ if (streq(name, "SloppyOptions"))
+ return bus_set_transient_bool(u, name, &m->sloppy_options, message, flags, error);
+
+ if (streq(name, "LazyUnmount"))
+ return bus_set_transient_bool(u, name, &m->lazy_unmount, message, flags, error);
+
+ if (streq(name, "ForceUnmount"))
+ return bus_set_transient_bool(u, name, &m->force_unmount, message, flags, error);
+
+ return 0;
}
int bus_mount_set_property(
diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c
index 0f54b04f76..b3f502f4c9 100644
--- a/src/core/dbus-path.c
+++ b/src/core/dbus-path.c
@@ -18,9 +18,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "bus-util.h"
#include "dbus-path.h"
+#include "dbus-util.h"
+#include "list.h"
#include "path.h"
+#include "path-util.h"
#include "string-util.h"
#include "unit.h"
@@ -85,3 +89,108 @@ const sd_bus_vtable bus_path_vtable[] = {
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Path, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_VTABLE_END
};
+
+static int bus_path_set_transient_property(
+ Path *p,
+ const char *name,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
+
+ Unit *u = UNIT(p);
+ int r;
+
+ assert(p);
+ assert(name);
+ assert(message);
+
+ flags |= UNIT_PRIVATE;
+
+ if (streq(name, "MakeDirectory"))
+ return bus_set_transient_bool(u, name, &p->make_directory, message, flags, error);
+
+ if (streq(name, "DirectoryMode"))
+ return bus_set_transient_mode_t(u, name, &p->directory_mode, message, flags, error);
+
+ if (streq(name, "Paths")) {
+ const char *type_name, *path;
+ bool empty = true;
+
+ r = sd_bus_message_enter_container(message, 'a', "(ss)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(message, "(ss)", &type_name, &path)) > 0) {
+ PathType t;
+
+ t = path_type_from_string(type_name);
+ if (t < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown path type: %s", type_name);
+
+ if (isempty(path))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path in %s is empty", type_name);
+
+ if (!path_is_absolute(path))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path in %s is not absolute: %s", type_name, path);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ _cleanup_free_ char *k;
+ PathSpec *s;
+
+ k = strdup(path);
+ if (!k)
+ return -ENOMEM;
+
+ s = new0(PathSpec, 1);
+ if (!s)
+ return -ENOMEM;
+
+ s->unit = u;
+ s->path = path_kill_slashes(k);
+ k = NULL;
+ s->type = t;
+ s->inotify_fd = -1;
+
+ LIST_PREPEND(spec, p->specs, s);
+
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", type_name, path);
+ }
+
+ empty = false;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) {
+ path_free_specs(p);
+ unit_write_settingf(u, flags, name, "PathExists=");
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int bus_path_set_property(
+ Unit *u,
+ const char *name,
+ sd_bus_message *message,
+ UnitWriteFlags mode,
+ sd_bus_error *error) {
+
+ Path *p = PATH(u);
+
+ assert(p);
+ assert(name);
+ assert(message);
+
+ if (u->transient && u->load_state == UNIT_STUB)
+ return bus_path_set_transient_property(p, name, message, mode, error);
+
+ return 0;
+}
diff --git a/src/core/dbus-path.h b/src/core/dbus-path.h
index 5e7e859b56..ccd88c7f86 100644
--- a/src/core/dbus-path.h
+++ b/src/core/dbus-path.h
@@ -20,6 +20,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "sd-bus.h"
+#include "unit.h"
extern const sd_bus_vtable bus_path_vtable[];
+
+int bus_path_set_property(Unit *u, const char *name, sd_bus_message *i, UnitWriteFlags flags, sd_bus_error *error);
diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c
index 9195ad36d0..a0c4a65b33 100644
--- a/src/core/dbus-scope.c
+++ b/src/core/dbus-scope.c
@@ -26,6 +26,7 @@
#include "dbus-kill.h"
#include "dbus-scope.h"
#include "dbus-unit.h"
+#include "dbus-util.h"
#include "dbus.h"
#include "scope.h"
#include "selinux-access.h"
@@ -84,6 +85,9 @@ static int bus_scope_set_transient_property(
flags |= UNIT_PRIVATE;
+ if (streq(name, "TimeoutStopUSec"))
+ return bus_set_transient_usec(UNIT(s), name, &s->timeout_stop_usec, message, flags, error);
+
if (streq(name, "PIDs")) {
unsigned n = 0;
uint32_t pid;
@@ -139,21 +143,6 @@ static int bus_scope_set_transient_property(
}
return 1;
-
- } else if (streq(name, "TimeoutStopUSec")) {
- uint64_t t;
-
- r = sd_bus_message_read(message, "t", &t);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- s->timeout_stop_usec = t;
-
- unit_write_settingf(UNIT(s), flags, name, "TimeoutStopSec=" USEC_FMT "us", t);
- }
-
- return 1;
}
return 0;
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index 0189952124..6de905b69c 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -22,15 +22,20 @@
#include "alloc-util.h"
#include "async.h"
+#include "bus-internal.h"
#include "bus-util.h"
#include "dbus-cgroup.h"
#include "dbus-execute.h"
#include "dbus-kill.h"
#include "dbus-service.h"
+#include "dbus-util.h"
+#include "exit-status.h"
#include "fd-util.h"
#include "fileio.h"
+#include "parse-util.h"
#include "path-util.h"
#include "service.h"
+#include "signal-util.h"
#include "string-util.h"
#include "strv.h"
#include "unit.h"
@@ -41,6 +46,71 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, Servi
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_notify_access, notify_access, NotifyAccess);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction);
+static int property_get_exit_status_set(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExitStatusSet *status_set = userdata;
+ Iterator i;
+ void *id;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(status_set);
+
+ r = sd_bus_message_open_container(reply, 'r', "aiai");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "i");
+ if (r < 0)
+ return r;
+
+ SET_FOREACH(id, status_set->status, i) {
+ int val = PTR_TO_INT(id);
+
+ if (val < 0 || val > 255)
+ continue;
+
+ r = sd_bus_message_append_basic(reply, 'i', &val);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "i");
+ if (r < 0)
+ return r;
+
+ SET_FOREACH(id, status_set->signal, i) {
+ int val = PTR_TO_INT(id);
+ const char *str;
+
+ str = signal_to_string(val);
+ if (!str)
+ continue;
+
+ r = sd_bus_message_append_basic(reply, 'i', &val);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_close_container(reply);
+}
+
const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Service, type), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -57,6 +127,9 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemainAfterExit", "b", bus_property_get_bool, offsetof(Service, remain_after_exit), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("GuessMainPID", "b", bus_property_get_bool, offsetof(Service, guess_main_pid), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RestartPreventExitStatus", "(aiai)", property_get_exit_status_set, offsetof(Service, restart_prevent_status), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RestartForceExitStatus", "(aiai)", property_get_exit_status_set, offsetof(Service, restart_force_status), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("SuccessExitStatus", "(aiai)", property_get_exit_status_set, offsetof(Service, success_status), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MainPID", "u", bus_property_get_pid, offsetof(Service, main_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Service, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("BusName", "s", NULL, offsetof(Service, bus_name), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -88,265 +161,210 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_VTABLE_END
};
-static int bus_service_set_transient_property(
- Service *s,
+static int bus_set_transient_exit_status(
+ Unit *u,
const char *name,
+ ExitStatusSet *status_set,
sd_bus_message *message,
UnitWriteFlags flags,
sd_bus_error *error) {
- ServiceExecCommand ci;
+ const int *status, *signal;
+ size_t sz_status, sz_signal, i;
int r;
- assert(s);
- assert(name);
- assert(message);
-
- flags |= UNIT_PRIVATE;
+ r = sd_bus_message_enter_container(message, 'r', "aiai");
+ if (r < 0)
+ return r;
- if (streq(name, "RemainAfterExit")) {
- int b;
+ r = sd_bus_message_read_array(message, 'i', (const void **) &status, &sz_status);
+ if (r < 0)
+ return r;
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
+ r = sd_bus_message_read_array(message, 'i', (const void **) &signal, &sz_signal);
+ if (r < 0)
+ return r;
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- s->remain_after_exit = b;
- unit_write_settingf(UNIT(s), flags, name, "RemainAfterExit=%s", yes_no(b));
- }
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+ if (sz_status == 0 && sz_signal == 0 && !UNIT_WRITE_FLAGS_NOOP(flags)) {
+ exit_status_set_free(status_set);
+ unit_write_settingf(u, flags, name, "%s=", name);
return 1;
+ }
- } else if (streq(name, "Type")) {
- const char *t;
- ServiceType k;
-
- r = sd_bus_message_read(message, "s", &t);
- if (r < 0)
- return r;
-
- k = service_type_from_string(t);
- if (k < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service type %s", t);
+ for (i = 0; i < sz_status; i++) {
+ if (status[i] < 0 || status[i] > 255)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid status code in %s: %i", name, status[i]);
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- s->type = k;
- unit_write_settingf(UNIT(s), flags, name, "Type=%s", service_type_to_string(s->type));
- }
-
- return 1;
- } else if (streq(name, "RuntimeMaxUSec")) {
- usec_t u;
+ r = set_ensure_allocated(&status_set->status, NULL);
+ if (r < 0)
+ return r;
- r = sd_bus_message_read(message, "t", &u);
- if (r < 0)
- return r;
+ r = set_put(status_set->status, INT_TO_PTR(status[i]));
+ if (r < 0)
+ return r;
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- s->runtime_max_usec = u;
- unit_write_settingf(UNIT(s), flags, name, "RuntimeMaxSec=" USEC_FMT "us", u);
+ unit_write_settingf(u, flags, name, "%s=%i", name, status[i]);
}
+ }
- return 1;
-
- } else if (streq(name, "Restart")) {
- ServiceRestart sr;
- const char *v;
-
- r = sd_bus_message_read(message, "s", &v);
- if (r < 0)
- return r;
+ for (i = 0; i < sz_signal; i++) {
+ const char *str;
- if (isempty(v))
- sr = SERVICE_RESTART_NO;
- else {
- sr = service_restart_from_string(v);
- if (sr < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid restart setting: %s", v);
- }
+ str = signal_to_string(signal[i]);
+ if (!str)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal in %s: %i", name, signal[i]);
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- s->restart = sr;
- unit_write_settingf(UNIT(s), flags, name, "Restart=%s", service_restart_to_string(sr));
- }
-
- return 1;
-
- } else if (STR_IN_SET(name,
- "StandardInputFileDescriptor",
- "StandardOutputFileDescriptor",
- "StandardErrorFileDescriptor")) {
- int fd;
+ r = set_ensure_allocated(&status_set->signal, NULL);
+ if (r < 0)
+ return r;
- r = sd_bus_message_read(message, "h", &fd);
- if (r < 0)
- return r;
+ r = set_put(status_set->signal, INT_TO_PTR(signal[i]));
+ if (r < 0)
+ return r;
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- int copy;
-
- copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (copy < 0)
- return -errno;
-
- if (streq(name, "StandardInputFileDescriptor")) {
- asynchronous_close(s->stdin_fd);
- s->stdin_fd = copy;
- } else if (streq(name, "StandardOutputFileDescriptor")) {
- asynchronous_close(s->stdout_fd);
- s->stdout_fd = copy;
- } else {
- asynchronous_close(s->stderr_fd);
- s->stderr_fd = copy;
- }
-
- s->exec_context.stdio_as_fds = true;
+ unit_write_settingf(u, flags, name, "%s=%s", name, str);
}
+ }
- return 1;
-
- } else if (streq(name, "FileDescriptorStoreMax")) {
- uint32_t u;
+ return 1;
+}
- r = sd_bus_message_read(message, "u", &u);
- if (r < 0)
- return r;
+static int bus_set_transient_std_fd(
+ Unit *u,
+ const char *name,
+ int *p,
+ bool *b,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- s->n_fd_store_max = (unsigned) u;
- unit_write_settingf(UNIT(s), flags, name, "FileDescriptorStoreMax=%" PRIu32, u);
- }
+ int fd, r;
- return 1;
+ assert(p);
+ assert(b);
- } else if (streq(name, "NotifyAccess")) {
- const char *t;
- NotifyAccess k;
+ r = sd_bus_message_read(message, "h", &fd);
+ if (r < 0)
+ return r;
- r = sd_bus_message_read(message, "s", &t);
- if (r < 0)
- return r;
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ int copy;
- k = notify_access_from_string(t);
- if (k < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid notify access setting %s", t);
+ copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (copy < 0)
+ return -errno;
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- s->notify_access = k;
- unit_write_settingf(UNIT(s), flags, name, "NotifyAccess=%s", notify_access_to_string(s->notify_access));
- }
+ asynchronous_close(*p);
+ *p = copy;
+ *b = true;
+ }
- return 1;
+ return 1;
+}
+static BUS_DEFINE_SET_TRANSIENT_PARSE(notify_access, NotifyAccess, notify_access_from_string);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(service_type, ServiceType, service_type_from_string);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(service_restart, ServiceRestart, service_restart_from_string);
+static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(bus_name, service_name_is_valid);
- } else if ((ci = service_exec_command_from_string(name)) >= 0) {
- unsigned n = 0;
+static int bus_service_set_transient_property(
+ Service *s,
+ const char *name,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
- r = sd_bus_message_enter_container(message, 'a', "(sasb)");
- if (r < 0)
- return r;
+ Unit *u = UNIT(s);
+ ServiceExecCommand ci;
+ int r;
- while ((r = sd_bus_message_enter_container(message, 'r', "sasb")) > 0) {
- _cleanup_strv_free_ char **argv = NULL;
- const char *path;
- int b;
+ assert(s);
+ assert(name);
+ assert(message);
- r = sd_bus_message_read(message, "s", &path);
- if (r < 0)
- return r;
+ flags |= UNIT_PRIVATE;
- if (!path_is_absolute(path))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path);
+ if (streq(name, "PermissionsStartOnly"))
+ return bus_set_transient_bool(u, name, &s->permissions_start_only, message, flags, error);
- r = sd_bus_message_read_strv(message, &argv);
- if (r < 0)
- return r;
+ if (streq(name, "RootDirectoryStartOnly"))
+ return bus_set_transient_bool(u, name, &s->root_directory_start_only, message, flags, error);
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
+ if (streq(name, "RemainAfterExit"))
+ return bus_set_transient_bool(u, name, &s->remain_after_exit, message, flags, error);
- r = sd_bus_message_exit_container(message);
- if (r < 0)
- return r;
+ if (streq(name, "GuessMainPID"))
+ return bus_set_transient_bool(u, name, &s->guess_main_pid, message, flags, error);
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- ExecCommand *c;
+ if (streq(name, "Type"))
+ return bus_set_transient_service_type(u, name, &s->type, message, flags, error);
- c = new0(ExecCommand, 1);
- if (!c)
- return -ENOMEM;
+ if (streq(name, "RestartUSec"))
+ return bus_set_transient_usec(u, name, &s->restart_usec, message, flags, error);
- c->path = strdup(path);
- if (!c->path) {
- free(c);
- return -ENOMEM;
- }
+ if (streq(name, "TimeoutStartUSec")) {
+ r = bus_set_transient_usec(u, name, &s->timeout_start_usec, message, flags, error);
+ if (r >= 0 && !UNIT_WRITE_FLAGS_NOOP(flags))
+ s->start_timeout_defined = true;
- c->argv = argv;
- argv = NULL;
+ return r;
+ }
- c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0;
+ if (streq(name, "TimeoutStopUSec"))
+ return bus_set_transient_usec(u, name, &s->timeout_stop_usec, message, flags, error);
- path_kill_slashes(c->path);
- exec_command_append_list(&s->exec_command[ci], c);
- }
+ if (streq(name, "RuntimeMaxUSec"))
+ return bus_set_transient_usec(u, name, &s->runtime_max_usec, message, flags, error);
- n++;
- }
+ if (streq(name, "WatchdogUSec"))
+ return bus_set_transient_usec(u, name, &s->watchdog_usec, message, flags, error);
- if (r < 0)
- return r;
+ if (streq(name, "FileDescriptorStoreMax"))
+ return bus_set_transient_unsigned(u, name, &s->n_fd_store_max, message, flags, error);
- r = sd_bus_message_exit_container(message);
- if (r < 0)
- return r;
+ if (streq(name, "NotifyAccess"))
+ return bus_set_transient_notify_access(u, name, &s->notify_access, message, flags, error);
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- _cleanup_free_ char *buf = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- ExecCommand *c;
- size_t size = 0;
+ if (streq(name, "PIDFile"))
+ return bus_set_transient_path(u, name, &s->pid_file, message, flags, error);
- if (n == 0)
- s->exec_command[ci] = exec_command_free_list(s->exec_command[ci]);
+ if (streq(name, "USBFunctionDescriptors"))
+ return bus_set_transient_path(u, name, &s->usb_function_descriptors, message, flags, error);
- f = open_memstream(&buf, &size);
- if (!f)
- return -ENOMEM;
+ if (streq(name, "USBFunctionStrings"))
+ return bus_set_transient_path(u, name, &s->usb_function_strings, message, flags, error);
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+ if (streq(name, "BusName"))
+ return bus_set_transient_bus_name(u, name, &s->bus_name, message, flags, error);
- fputs("ExecStart=\n", f);
+ if (streq(name, "Restart"))
+ return bus_set_transient_service_restart(u, name, &s->restart, message, flags, error);
- LIST_FOREACH(command, c, s->exec_command[ci]) {
- _cleanup_free_ char *a = NULL, *t = NULL;
- const char *p;
+ if (streq(name, "RestartPreventExitStatus"))
+ return bus_set_transient_exit_status(u, name, &s->restart_prevent_status, message, flags, error);
- p = unit_escape_setting(c->path, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS, &t);
- if (!p)
- return -ENOMEM;
+ if (streq(name, "RestartForceExitStatus"))
+ return bus_set_transient_exit_status(u, name, &s->restart_force_status, message, flags, error);
- a = unit_concat_strv(c->argv, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS);
- if (!a)
- return -ENOMEM;
+ if (streq(name, "SuccessExitStatus"))
+ return bus_set_transient_exit_status(u, name, &s->success_status, message, flags, error);
- fprintf(f, "%s=%s@%s %s\n",
- name,
- c->flags & EXEC_COMMAND_IGNORE_FAILURE ? "-" : "",
- p,
- a);
- }
+ if ((ci = service_exec_command_from_string(name)) >= 0)
+ return bus_set_transient_exec_command(u, name, &s->exec_command[ci], message, flags, error);
- r = fflush_and_check(f);
- if (r < 0)
- return r;
+ if (streq(name, "StandardInputFileDescriptor"))
+ return bus_set_transient_std_fd(u, name, &s->stdin_fd, &s->exec_context.stdio_as_fds, message, flags, error);
- unit_write_setting(UNIT(s), flags, name, buf);
- }
+ if (streq(name, "StandardOutputFileDescriptor"))
+ return bus_set_transient_std_fd(u, name, &s->stdout_fd, &s->exec_context.stdio_as_fds, message, flags, error);
- return 1;
- }
+ if (streq(name, "StandardErrorFileDescriptor"))
+ return bus_set_transient_std_fd(u, name, &s->stderr_fd, &s->exec_context.stdio_as_fds, message, flags, error);
return 0;
}
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index 930b7fa87d..035651f213 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -22,8 +22,15 @@
#include "bus-util.h"
#include "dbus-cgroup.h"
#include "dbus-execute.h"
+#include "dbus-kill.h"
#include "dbus-socket.h"
+#include "dbus-util.h"
+#include "fd-util.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "socket.h"
+#include "socket-protocol-list.h"
+#include "socket-util.h"
#include "string-util.h"
#include "unit.h"
@@ -141,6 +148,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("MaxConnectionsPerSource", "u", bus_property_get_unsigned, offsetof(Socket, max_connections_per_source), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MessageQueueMaxMessages", "x", bus_property_get_long, offsetof(Socket, mq_maxmsg), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MessageQueueMessageSize", "x", bus_property_get_long, offsetof(Socket, mq_msgsize), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("TCPCongestion", "s", NULL, offsetof(Socket, tcp_congestion), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ReusePort", "b", bus_property_get_bool, offsetof(Socket, reuse_port), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SmackLabel", "s", NULL, offsetof(Socket, smack), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SmackLabelIPIn", "s", NULL, offsetof(Socket, smack_ip_in), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -162,6 +170,286 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_VTABLE_END
};
+static inline bool check_size_t_truncation(uint64_t t) {
+ return (size_t) t == t;
+}
+
+static inline const char* socket_protocol_to_name_supported(int32_t i) {
+ if (!IN_SET(i, IPPROTO_UDPLITE, IPPROTO_SCTP))
+ return NULL;
+
+ return socket_protocol_to_name(i);
+}
+
+static BUS_DEFINE_SET_TRANSIENT(int, "i", int32_t, int, "%" PRIi32);
+static BUS_DEFINE_SET_TRANSIENT(message_queue, "x", int64_t, long, "%" PRIi64);
+static BUS_DEFINE_SET_TRANSIENT_IS_VALID(size_t_check_truncation, "t", uint64_t, size_t, "%" PRIu64, check_size_t_truncation);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(bind_ipv6_only, SocketAddressBindIPv6Only, parse_socket_address_bind_ipv6_only_or_bool);
+static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(fdname, fdname_is_valid);
+static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(ifname, ifname_valid);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(ip_tos, "i", int32_t, int, "%" PRIi32, ip_tos_to_string_alloc);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING(socket_protocol, "i", int32_t, int, "%" PRIi32, socket_protocol_to_name_supported);
+
+static int bus_socket_set_transient_property(
+ Socket *s,
+ const char *name,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
+
+ SocketExecCommand ci;
+ Unit *u = UNIT(s);
+ int r;
+
+ assert(s);
+ assert(name);
+ assert(message);
+
+ flags |= UNIT_PRIVATE;
+
+ if (streq(name, "Accept"))
+ return bus_set_transient_bool(u, name, &s->accept, message, flags, error);
+
+ if (streq(name, "Writable"))
+ return bus_set_transient_bool(u, name, &s->writable, message, flags, error);
+
+ if (streq(name, "KeepAlive"))
+ return bus_set_transient_bool(u, name, &s->keep_alive, message, flags, error);
+
+ if (streq(name, "NoDelay"))
+ return bus_set_transient_bool(u, name, &s->no_delay, message, flags, error);
+
+ if (streq(name, "FreeBind"))
+ return bus_set_transient_bool(u, name, &s->free_bind, message, flags, error);
+
+ if (streq(name, "Transparent"))
+ return bus_set_transient_bool(u, name, &s->transparent, message, flags, error);
+
+ if (streq(name, "Broadcast"))
+ return bus_set_transient_bool(u, name, &s->broadcast, message, flags, error);
+
+ if (streq(name, "PassCredentials"))
+ return bus_set_transient_bool(u, name, &s->pass_cred, message, flags, error);
+
+ if (streq(name, "PassSecurity"))
+ return bus_set_transient_bool(u, name, &s->pass_sec, message, flags, error);
+
+ if (streq(name, "ReusePort"))
+ return bus_set_transient_bool(u, name, &s->reuse_port, message, flags, error);
+
+ if (streq(name, "RemoveOnStop"))
+ return bus_set_transient_bool(u, name, &s->remove_on_stop, message, flags, error);
+
+ if (streq(name, "SELinuxContextFromNet"))
+ return bus_set_transient_bool(u, name, &s->selinux_context_from_net, message, flags, error);
+
+ if (streq(name, "Priority"))
+ return bus_set_transient_int(u, name, &s->priority, message, flags, error);
+
+ if (streq(name, "IPTTL"))
+ return bus_set_transient_int(u, name, &s->ip_ttl, message, flags, error);
+
+ if (streq(name, "Mark"))
+ return bus_set_transient_int(u, name, &s->mark, message, flags, error);
+
+ if (streq(name, "Backlog"))
+ return bus_set_transient_unsigned(u, name, &s->backlog, message, flags, error);
+
+ if (streq(name, "MaxConnections"))
+ return bus_set_transient_unsigned(u, name, &s->max_connections, message, flags, error);
+
+ if (streq(name, "MaxConnectionsPerSource"))
+ return bus_set_transient_unsigned(u, name, &s->max_connections_per_source, message, flags, error);
+
+ if (streq(name, "KeepAliveProbes"))
+ return bus_set_transient_unsigned(u, name, &s->keep_alive_cnt, message, flags, error);
+
+ if (streq(name, "TriggerLimitBurst"))
+ return bus_set_transient_unsigned(u, name, &s->trigger_limit.burst, message, flags, error);
+
+ if (streq(name, "SocketMode"))
+ return bus_set_transient_mode_t(u, name, &s->socket_mode, message, flags, error);
+
+ if (streq(name, "DirectoryMode"))
+ return bus_set_transient_mode_t(u, name, &s->directory_mode, message, flags, error);
+
+ if (streq(name, "MessageQueueMaxMessages"))
+ return bus_set_transient_message_queue(u, name, &s->mq_maxmsg, message, flags, error);
+
+ if (streq(name, "MessageQueueMessageSize"))
+ return bus_set_transient_message_queue(u, name, &s->mq_msgsize, message, flags, error);
+
+ if (streq(name, "TimeoutUSec"))
+ return bus_set_transient_usec_fix_0(u, name, &s->timeout_usec, message, flags, error);
+
+ if (streq(name, "KeepAliveTimeUSec"))
+ return bus_set_transient_usec(u, name, &s->keep_alive_time, message, flags, error);
+
+ if (streq(name, "KeepAliveIntervalUSec"))
+ return bus_set_transient_usec(u, name, &s->keep_alive_interval, message, flags, error);
+
+ if (streq(name, "DeferAcceptUSec"))
+ return bus_set_transient_usec(u, name, &s->defer_accept, message, flags, error);
+
+ if (streq(name, "TriggerLimitIntervalUSec"))
+ return bus_set_transient_usec(u, name, &s->trigger_limit.interval, message, flags, error);
+
+ if (streq(name, "SmackLabel"))
+ return bus_set_transient_string(u, name, &s->smack, message, flags, error);
+
+ if (streq(name, "SmackLabelIPin"))
+ return bus_set_transient_string(u, name, &s->smack_ip_in, message, flags, error);
+
+ if (streq(name, "SmackLabelIPOut"))
+ return bus_set_transient_string(u, name, &s->smack_ip_out, message, flags, error);
+
+ if (streq(name, "TCPCongestion"))
+ return bus_set_transient_string(u, name, &s->tcp_congestion, message, flags, error);
+
+ if (streq(name, "FileDescriptorName"))
+ return bus_set_transient_fdname(u, name, &s->fdname, message, flags, error);
+
+ if (streq(name, "SocketUser"))
+ return bus_set_transient_user(u, name, &s->user, message, flags, error);
+
+ if (streq(name, "SocketGroup"))
+ return bus_set_transient_user(u, name, &s->group, message, flags, error);
+
+ if (streq(name, "BindIPv6Only"))
+ return bus_set_transient_bind_ipv6_only(u, name, &s->bind_ipv6_only, message, flags, error);
+
+ if (streq(name, "ReceiveBuffer"))
+ return bus_set_transient_size_t_check_truncation(u, name, &s->receive_buffer, message, flags, error);
+
+ if (streq(name, "SendBuffer"))
+ return bus_set_transient_size_t_check_truncation(u, name, &s->send_buffer, message, flags, error);
+
+ if (streq(name, "PipeSize"))
+ return bus_set_transient_size_t_check_truncation(u, name, &s->pipe_size, message, flags, error);
+
+ if (streq(name, "BindToDevice"))
+ return bus_set_transient_ifname(u, name, &s->bind_to_device, message, flags, error);
+
+ if (streq(name, "IPTOS"))
+ return bus_set_transient_ip_tos(u, name, &s->ip_tos, message, flags, error);
+
+ if (streq(name, "SocketProtocol"))
+ return bus_set_transient_socket_protocol(u, name, &s->socket_protocol, message, flags, error);
+
+ if ((ci = socket_exec_command_from_string(name)) >= 0)
+ return bus_set_transient_exec_command(u, name, &s->exec_command[ci], message, flags, error);
+
+ if (streq(name, "Symlinks")) {
+ _cleanup_strv_free_ char **l = NULL;
+ char **p;
+
+ r = sd_bus_message_read_strv(message, &l);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(p, l) {
+ if (!path_is_absolute(*p))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Symlink path is not absolute: %s", *p);
+ }
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ if (strv_isempty(l)) {
+ s->symlinks = strv_free(s->symlinks);
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=", name);
+ } else {
+ _cleanup_free_ char *joined = NULL;
+
+ r = strv_extend_strv(&s->symlinks, l, true);
+ if (r < 0)
+ return -ENOMEM;
+
+ joined = strv_join(l, " ");
+ if (!joined)
+ return -ENOMEM;
+
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, joined);
+ }
+ }
+
+ return 1;
+
+ } else if (streq(name, "Listen")) {
+ const char *t, *a;
+ bool empty = true;
+
+ r = sd_bus_message_enter_container(message, 'a', "(ss)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(message, "(ss)", &t, &a)) > 0) {
+ _cleanup_free_ SocketPort *p = NULL;
+
+ p = new0(SocketPort, 1);
+ if (!p)
+ return log_oom();
+
+ p->type = socket_port_type_from_string(t);
+ if (p->type < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown Socket type: %s", t);
+
+ if (p->type != SOCKET_SOCKET) {
+ p->path = strdup(a);
+ path_kill_slashes(p->path);
+
+ } else if (streq(t, "Netlink")) {
+ r = socket_address_parse_netlink(&p->address, a);
+ if (r < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid netlink address: %s", a);
+
+ } else {
+ r = socket_address_parse(&p->address, a);
+ if (r < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address: %s", a);
+
+ p->address.type = socket_address_type_from_string(t);
+ if (p->address.type < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address type: %s", t);
+
+ if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Address family not supported: %s", a);
+ }
+
+ p->fd = -1;
+ p->auxiliary_fds = NULL;
+ p->n_auxiliary_fds = 0;
+ p->socket = s;
+
+ empty = false;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ SocketPort *tail;
+
+ LIST_FIND_TAIL(port, s->ports, tail);
+ LIST_INSERT_AFTER(port, s->ports, tail, p);
+
+ p = NULL;
+
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "Listen%s=%s", t, a);
+ }
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) {
+ socket_free_ports(s);
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "ListenStream=");
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
int bus_socket_set_property(
Unit *u,
const char *name,
@@ -170,12 +458,37 @@ int bus_socket_set_property(
sd_bus_error *error) {
Socket *s = SOCKET(u);
+ int r;
assert(s);
assert(name);
assert(message);
- return bus_cgroup_set_property(u, &s->cgroup_context, name, message, flags, error);
+ assert(s);
+ assert(name);
+ assert(message);
+
+ r = bus_cgroup_set_property(u, &s->cgroup_context, name, message, flags, error);
+ if (r != 0)
+ return r;
+
+ if (u->transient && u->load_state == UNIT_STUB) {
+ /* This is a transient unit, let's load a little more */
+
+ r = bus_socket_set_transient_property(s, name, message, flags, error);
+ if (r != 0)
+ return r;
+
+ r = bus_exec_context_set_transient_property(u, &s->exec_context, name, message, flags, error);
+ if (r != 0)
+ return r;
+
+ r = bus_kill_context_set_transient_property(u, &s->kill_context, name, message, flags, error);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
}
int bus_socket_commit_properties(Unit *u) {
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
index 3e64536b17..1eedf217fe 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -21,6 +21,7 @@
#include "alloc-util.h"
#include "bus-util.h"
#include "dbus-timer.h"
+#include "dbus-util.h"
#include "strv.h"
#include "timer.h"
#include "unit.h"
@@ -179,6 +180,7 @@ static int bus_timer_set_transient_property(
UnitWriteFlags flags,
sd_bus_error *error) {
+ Unit *u = UNIT(t);
int r;
assert(t);
@@ -187,7 +189,130 @@ static int bus_timer_set_transient_property(
flags |= UNIT_PRIVATE;
- if (STR_IN_SET(name,
+ if (streq(name, "AccuracyUSec"))
+ return bus_set_transient_usec(u, name, &t->accuracy_usec, message, flags, error);
+
+ if (streq(name, "AccuracySec")) {
+ log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead.");
+ return bus_set_transient_usec(u, "AccuracyUSec", &t->accuracy_usec, message, flags, error);
+ }
+
+ if (streq(name, "RandomizedDelayUSec"))
+ return bus_set_transient_usec(u, name, &t->random_usec, message, flags, error);
+
+ if (streq(name, "WakeSystem"))
+ return bus_set_transient_bool(u, name, &t->wake_system, message, flags, error);
+
+ if (streq(name, "Persistent"))
+ return bus_set_transient_bool(u, name, &t->persistent, message, flags, error);
+
+ if (streq(name, "RemainAfterElapse"))
+ return bus_set_transient_bool(u, name, &t->remain_after_elapse, message, flags, error);
+
+ if (streq(name, "TimersMonotonic")) {
+ const char *base_name;
+ usec_t usec = 0;
+ bool empty = true;
+
+ r = sd_bus_message_enter_container(message, 'a', "(st)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(message, "(st)", &base_name, &usec)) > 0) {
+ TimerBase b;
+
+ b = timer_base_from_string(base_name);
+ if (b < 0 || b == TIMER_CALENDAR)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid timer base: %s", base_name);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ TimerValue *v;
+
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", base_name,
+ format_timespan(ts, sizeof(ts), usec, USEC_PER_MSEC));
+
+ v = new0(TimerValue, 1);
+ if (!v)
+ return -ENOMEM;
+
+ v->base = b;
+ v->value = usec;
+
+ LIST_PREPEND(value, t->values, v);
+ }
+
+ empty = false;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) {
+ timer_free_values(t);
+ unit_write_setting(u, flags, name, "OnActiveSec=");
+ }
+
+ return 1;
+
+ } else if (streq(name, "TimersCalendar")) {
+ const char *base_name, *str;
+ bool empty = true;
+
+ r = sd_bus_message_enter_container(message, 'a', "(ss)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(message, "(ss)", &base_name, &str)) > 0) {
+ _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL;
+ TimerBase b;
+
+ b = timer_base_from_string(base_name);
+ if (b != TIMER_CALENDAR)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid timer base: %s", base_name);
+
+ r = calendar_spec_from_string(str, &c);
+ if (r == -EINVAL)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid calendar spec: %s", str);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ TimerValue *v;
+
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", base_name, str);
+
+ v = new0(TimerValue, 1);
+ if (!v)
+ return -ENOMEM;
+
+ v->base = b;
+ v->calendar_spec = c;
+ c = NULL;
+
+ LIST_PREPEND(value, t->values, v);
+ }
+
+ empty = false;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) {
+ timer_free_values(t);
+ unit_write_setting(u, flags, name, "OnCalendar=");
+ }
+
+ return 1;
+
+ } else if (STR_IN_SET(name,
"OnActiveSec",
"OnBootSec",
"OnStartupSec",
@@ -196,27 +321,30 @@ static int bus_timer_set_transient_property(
TimerValue *v;
TimerBase b = _TIMER_BASE_INVALID;
- usec_t u = 0;
+ usec_t usec = 0;
+
+ log_notice("Client is using obsolete %s= transient property, please use TimersMonotonic= instead.", name);
b = timer_base_from_string(name);
if (b < 0)
- return -EINVAL;
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown timer base");
- r = sd_bus_message_read(message, "t", &u);
+ r = sd_bus_message_read(message, "t", &usec);
if (r < 0)
return r;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
char time[FORMAT_TIMESPAN_MAX];
- unit_write_settingf(UNIT(t), flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, format_timespan(time, sizeof(time), u, USEC_PER_MSEC));
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name,
+ format_timespan(time, sizeof(time), usec, USEC_PER_MSEC));
v = new0(TimerValue, 1);
if (!v)
return -ENOMEM;
v->base = b;
- v->value = u;
+ v->value = usec;
LIST_PREPEND(value, t->values, v);
}
@@ -226,84 +354,36 @@ static int bus_timer_set_transient_property(
} else if (streq(name, "OnCalendar")) {
TimerValue *v;
- CalendarSpec *c = NULL;
+ _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL;
const char *str;
+ log_notice("Client is using obsolete %s= transient property, please use TimersCalendar= instead.", name);
+
r = sd_bus_message_read(message, "s", &str);
if (r < 0)
return r;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
r = calendar_spec_from_string(str, &c);
+ if (r == -EINVAL)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid calendar spec");
if (r < 0)
return r;
- unit_write_settingf(UNIT(t), flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, str);
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, str);
v = new0(TimerValue, 1);
- if (!v) {
- calendar_spec_free(c);
+ if (!v)
return -ENOMEM;
- }
v->base = TIMER_CALENDAR;
v->calendar_spec = c;
+ c = NULL;
LIST_PREPEND(value, t->values, v);
}
return 1;
-
- } else if (STR_IN_SET(name, "AccuracyUSec", "AccuracySec")) {
- usec_t u = 0;
-
- if (streq(name, "AccuracySec"))
- log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead.");
-
- r = sd_bus_message_read(message, "t", &u);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- t->accuracy_usec = u;
- unit_write_settingf(UNIT(t), flags, name, "AccuracySec=" USEC_FMT "us", u);
- }
-
- return 1;
-
- } else if (streq(name, "RandomizedDelayUSec")) {
- usec_t u = 0;
-
- r = sd_bus_message_read(message, "t", &u);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- t->random_usec = u;
- unit_write_settingf(UNIT(t), flags, name, "RandomizedDelaySec=" USEC_FMT "us", u);
- }
-
- return 1;
-
- } else if (STR_IN_SET(name, "WakeSystem", "Persistent", "RemainAfterElapse")) {
- int b;
-
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- if (streq(name, "WakeSystem"))
- t->wake_system = b;
- else if (streq(name, "Persistent"))
- t->persistent = b;
- else /* RemainAfterElapse */
- t->remain_after_elapse = b;
-
- unit_write_settingf(UNIT(t), flags, name, "%s=%s", name, yes_no(b));
- }
-
- return 1;
}
return 0;
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index cdab461d58..7085eee930 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -24,12 +24,15 @@
#include "bpf-firewall.h"
#include "bus-common-errors.h"
#include "cgroup-util.h"
+#include "condition.h"
#include "dbus-job.h"
#include "dbus-unit.h"
+#include "dbus-util.h"
#include "dbus.h"
#include "fd-util.h"
#include "locale-util.h"
#include "log.h"
+#include "path-util.h"
#include "process-util.h"
#include "selinux-access.h"
#include "signal-util.h"
@@ -37,6 +40,7 @@
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
+#include "web-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_collect_mode, collect_mode, CollectMode);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState);
@@ -795,7 +799,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Perpetual", "b", bus_property_get_bool, offsetof(Unit, perpetual), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StartLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("FailureAction", "s", property_get_emergency_action, offsetof(Unit, failure_action), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -823,6 +827,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_VTABLE_END
};
@@ -1352,6 +1357,81 @@ static int bus_unit_set_live_property(
return 0;
}
+static BUS_DEFINE_SET_TRANSIENT_PARSE(collect_mode, CollectMode, collect_mode_from_string);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(emergency_action, EmergencyAction, emergency_action_from_string);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(job_mode, JobMode, job_mode_from_string);
+
+static int bus_set_transient_conditions(
+ Unit *u,
+ const char *name,
+ Condition **list,
+ bool is_condition,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
+
+ const char *type_name, *param;
+ int trigger, negate, r;
+ bool empty = true;
+
+ assert(list);
+
+ r = sd_bus_message_enter_container(message, 'a', "(sbbs)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(message, "(sbbs)", &type_name, &trigger, &negate, &param)) > 0) {
+ ConditionType t;
+
+ t = is_condition ? condition_type_from_string(type_name) : assert_type_from_string(type_name);
+ if (t < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid condition type: %s", type_name);
+
+ if (t != CONDITION_NULL) {
+ if (isempty(param))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Condition parameter in %s is empty", type_name);
+
+ if (condition_takes_path(t) && !path_is_absolute(param))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path in condition %s is not absolute: %s", type_name, param);
+ } else
+ param = NULL;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ Condition *c;
+
+ c = condition_new(t, param, trigger, negate);
+ if (!c)
+ return -ENOMEM;
+
+ LIST_PREPEND(conditions, *list, c);
+
+ if (t != CONDITION_NULL)
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name,
+ "%s=%s%s%s", type_name,
+ trigger ? "|" : "", negate ? "!" : "", param);
+ else
+ unit_write_settingf(u, flags, name,
+ "%s=%s%s", type_name,
+ trigger ? "|" : "", yes_no(!negate));
+ }
+
+ empty = false;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) {
+ *list = condition_free_list(*list);
+ unit_write_settingf(u, flags, name, "%sNull=", is_condition ? "Condition" : "Assert");
+ }
+
+ return 1;
+}
+
static int bus_unit_set_transient_property(
Unit *u,
const char *name,
@@ -1359,6 +1439,7 @@ static int bus_unit_set_transient_property(
UnitWriteFlags flags,
sd_bus_error *error) {
+ UnitDependency d = _UNIT_DEPENDENCY_INVALID;
int r;
assert(u);
@@ -1368,35 +1449,100 @@ static int bus_unit_set_transient_property(
/* Handles settings when transient units are created. This settings cannot be altered anymore after the unit
* has been created. */
- if (streq(name, "DefaultDependencies")) {
- int b;
+ if (streq(name, "SourcePath"))
+ return bus_set_transient_path(u, name, &u->source_path, message, flags, error);
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
+ if (streq(name, "StopWhenUnneeded"))
+ return bus_set_transient_bool(u, name, &u->stop_when_unneeded, message, flags, error);
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- u->default_dependencies = b;
- unit_write_settingf(u, flags, name, "DefaultDependencies=%s", yes_no(b));
- }
+ if (streq(name, "RefuseManualStart"))
+ return bus_set_transient_bool(u, name, &u->refuse_manual_start, message, flags, error);
- return 1;
+ if (streq(name, "RefuseManualStop"))
+ return bus_set_transient_bool(u, name, &u->refuse_manual_stop, message, flags, error);
- } else if (streq(name, "CollectMode")) {
- const char *s;
- CollectMode m;
+ if (streq(name, "AllowIsolate"))
+ return bus_set_transient_bool(u, name, &u->allow_isolate, message, flags, error);
- r = sd_bus_message_read(message, "s", &s);
+ if (streq(name, "DefaultDependencies"))
+ return bus_set_transient_bool(u, name, &u->default_dependencies, message, flags, error);
+
+ if (streq(name, "OnFailureJobMode"))
+ return bus_set_transient_job_mode(u, name, &u->on_failure_job_mode, message, flags, error);
+
+ if (streq(name, "IgnoreOnIsolate"))
+ return bus_set_transient_bool(u, name, &u->ignore_on_isolate, message, flags, error);
+
+ if (streq(name, "JobTimeoutUSec")) {
+ r = bus_set_transient_usec_fix_0(u, name, &u->job_timeout, message, flags, error);
+ if (r >= 0 && !UNIT_WRITE_FLAGS_NOOP(flags) && !u->job_running_timeout_set)
+ u->job_running_timeout = u->job_timeout;
+ }
+
+ if (streq(name, "JobRunningTimeoutUSec")) {
+ r = bus_set_transient_usec_fix_0(u, name, &u->job_running_timeout, message, flags, error);
+ if (r >= 0 && !UNIT_WRITE_FLAGS_NOOP(flags))
+ u->job_running_timeout_set = true;
+
+ return r;
+ }
+
+ if (streq(name, "JobTimeoutAction"))
+ return bus_set_transient_emergency_action(u, name, &u->job_timeout_action, message, flags, error);
+
+ if (streq(name, "JobTimeoutRebootArgument"))
+ return bus_set_transient_string(u, name, &u->job_timeout_reboot_arg, message, flags, error);
+
+ if (streq(name, "StartLimitIntervalUSec"))
+ return bus_set_transient_usec(u, name, &u->start_limit.interval, message, flags, error);
+
+ if (streq(name, "StartLimitBurst"))
+ return bus_set_transient_unsigned(u, name, &u->start_limit.burst, message, flags, error);
+
+ if (streq(name, "StartLimitAction"))
+ return bus_set_transient_emergency_action(u, name, &u->start_limit_action, message, flags, error);
+
+ if (streq(name, "FailureAction"))
+ return bus_set_transient_emergency_action(u, name, &u->failure_action, message, flags, error);
+
+ if (streq(name, "SuccessAction"))
+ return bus_set_transient_emergency_action(u, name, &u->success_action, message, flags, error);
+
+ if (streq(name, "RebootArgument"))
+ return bus_set_transient_string(u, name, &u->reboot_arg, message, flags, error);
+
+ if (streq(name, "CollectMode"))
+ return bus_set_transient_collect_mode(u, name, &u->collect_mode, message, flags, error);
+
+ if (streq(name, "Conditions"))
+ return bus_set_transient_conditions(u, name, &u->conditions, true, message, flags, error);
+
+ if (streq(name, "Asserts"))
+ return bus_set_transient_conditions(u, name, &u->asserts, false, message, flags, error);
+
+ if (streq(name, "Documentation")) {
+ _cleanup_strv_free_ char **l = NULL;
+ char **p;
+
+ r = sd_bus_message_read_strv(message, &l);
if (r < 0)
return r;
- m = collect_mode_from_string(s);
- if (m < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown garbage collection mode: %s", s);
+ STRV_FOREACH(p, l) {
+ if (!documentation_url_is_valid(*p))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid URL in %s: %s", name, *p);
+ }
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- u->collect_mode = m;
- unit_write_settingf(u, flags, name, "CollectMode=%s", collect_mode_to_string(m));
+ if (strv_isempty(l)) {
+ u->documentation = strv_free(u->documentation);
+ unit_write_settingf(u, flags, name, "%s=", name);
+ } else {
+ strv_extend_strv(&u->documentation, l, false);
+
+ STRV_FOREACH(p, l)
+ unit_write_settingf(u, flags, name, "%s=%s", name, *p);
+ }
}
return 1;
@@ -1439,30 +1585,40 @@ static int bus_unit_set_transient_property(
return 1;
- } else if (STR_IN_SET(name,
- "Requires", "RequiresOverridable",
- "Requisite", "RequisiteOverridable",
- "Wants",
- "BindsTo",
- "Conflicts",
- "Before", "After",
- "OnFailure",
- "PropagatesReloadTo", "ReloadPropagatedFrom",
- "PartOf")) {
-
- UnitDependency d;
- const char *other;
+ } else if (streq(name, "RequiresMountsFor")) {
+ _cleanup_strv_free_ char **l = NULL;
+ char **p;
- if (streq(name, "RequiresOverridable"))
- d = UNIT_REQUIRES; /* redirect for obsolete unit dependency type */
- else if (streq(name, "RequisiteOverridable"))
- d = UNIT_REQUISITE; /* same here */
- else {
- d = unit_dependency_from_string(name);
- if (d < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit dependency: %s", name);
+ r = sd_bus_message_read_strv(message, &l);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(p, l) {
+ if (!path_is_absolute(*p))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path specified in %s is not absolute: %s", name, *p);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ r = unit_require_mounts_for(u, *p, UNIT_DEPENDENCY_FILE);
+ if (r < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to add required mount \"%s\": %m", *p);
+
+ unit_write_settingf(u, flags, name, "%s=%s", name, *p);
+ }
}
+ return 1;
+ }
+
+ if (streq(name, "RequiresOverridable"))
+ d = UNIT_REQUIRES; /* redirect for obsolete unit dependency type */
+ else if (streq(name, "RequisiteOverridable"))
+ d = UNIT_REQUISITE; /* same here */
+ else
+ d = unit_dependency_from_string(name);
+
+ if (d >= 0) {
+ const char *other;
+
r = sd_bus_message_enter_container(message, 'a', "s");
if (r < 0)
return r;
@@ -1482,7 +1638,7 @@ static int bus_unit_set_transient_property(
if (!label)
return -ENOMEM;
- unit_write_settingf(u, flags, label, "%s=%s", name, other);
+ unit_write_settingf(u, flags, label, "%s=%s", unit_dependency_to_string(d), other);
}
}
@@ -1495,30 +1651,6 @@ static int bus_unit_set_transient_property(
return 1;
- } else if (STR_IN_SET(name, "FailureAction", "SuccessAction")) {
- EmergencyAction action;
- const char *s;
-
- r = sd_bus_message_read(message, "s", &s);
- if (r < 0)
- return r;
-
- action = emergency_action_from_string(s);
- if (action < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid emergency action: %s", s);
-
- if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-
- if (streq(name, "FailureAction"))
- u->failure_action = action;
- else
- u->success_action = action;
-
- unit_write_settingf(u, flags, name, "%s=%s", name, emergency_action_to_string(action));
- }
-
- return 1;
-
} else if (streq(name, "AddRef")) {
int b;
diff --git a/src/core/dbus-util.c b/src/core/dbus-util.c
new file mode 100644
index 0000000000..75bbd07604
--- /dev/null
+++ b/src/core/dbus-util.c
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "bus-util.h"
+#include "dbus-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "unit-printf.h"
+#include "user-util.h"
+#include "unit.h"
+
+BUS_DEFINE_SET_TRANSIENT(mode_t, "u", uint32_t, mode_t, "%040o");
+BUS_DEFINE_SET_TRANSIENT(unsigned, "u", uint32_t, unsigned, "%" PRIu32);
+BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user, valid_user_group_name_or_id);
+BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(path, path_is_absolute);
+
+int bus_set_transient_string(
+ Unit *u,
+ const char *name,
+ char **p,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
+
+ const char *v;
+ int r;
+
+ assert(p);
+
+ r = sd_bus_message_read(message, "s", &v);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ r = free_and_strdup(p, empty_to_null(v));
+ if (r < 0)
+ return r;
+
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name,
+ "%s=%s", name, strempty(v));
+ }
+
+ return 1;
+}
+
+int bus_set_transient_bool(
+ Unit *u,
+ const char *name,
+ bool *p,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
+
+ int v, r;
+
+ assert(p);
+
+ r = sd_bus_message_read(message, "b", &v);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ *p = v;
+ unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(v));
+ }
+
+ return 1;
+}
+
+int bus_set_transient_usec_internal(
+ Unit *u,
+ const char *name,
+ usec_t *p,
+ bool fix_0,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
+
+ uint64_t v;
+ int r;
+
+ assert(p);
+
+ r = sd_bus_message_read(message, "t", &v);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ char *n, ts[FORMAT_TIMESPAN_MAX];
+
+ if (fix_0)
+ *p = v != 0 ? v: USEC_INFINITY;
+ else
+ *p = v;
+
+ n = strndupa(name, strlen(name) - 4);
+ unit_write_settingf(u, flags, name, "%sSec=%s", n,
+ format_timespan(ts, sizeof(ts), v, USEC_PER_MSEC));
+ }
+
+ return 1;
+}
diff --git a/src/core/dbus-util.h b/src/core/dbus-util.h
new file mode 100644
index 0000000000..8260298577
--- /dev/null
+++ b/src/core/dbus-util.h
@@ -0,0 +1,351 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+#include "unit.h"
+
+#define BUS_DEFINE_SET_TRANSIENT(function, bus_type, type, cast_type, fmt) \
+ int bus_set_transient_##function( \
+ Unit *u, \
+ const char *name, \
+ cast_type *p, \
+ sd_bus_message *message, \
+ UnitWriteFlags flags, \
+ sd_bus_error *error) { \
+ \
+ type v; \
+ int r; \
+ \
+ assert(p); \
+ \
+ r = sd_bus_message_read(message, bus_type, &v); \
+ if (r < 0) \
+ return r; \
+ \
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
+ *p = (cast_type) v; \
+ unit_write_settingf(u, flags, name, \
+ "%s=" fmt, name, v); \
+ } \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define BUS_DEFINE_SET_TRANSIENT_IS_VALID(function, bus_type, type, cast_type, fmt, check) \
+ int bus_set_transient_##function( \
+ Unit *u, \
+ const char *name, \
+ cast_type *p, \
+ sd_bus_message *message, \
+ UnitWriteFlags flags, \
+ sd_bus_error *error) { \
+ \
+ type v; \
+ int r; \
+ \
+ assert(p); \
+ \
+ r = sd_bus_message_read(message, bus_type, &v); \
+ if (r < 0) \
+ return r; \
+ \
+ if (!check(v)) \
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \
+ "Invalid %s setting: " fmt, name, v); \
+ \
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
+ *p = (cast_type) v; \
+ unit_write_settingf(u, flags, name, \
+ "%s=" fmt, name, v); \
+ } \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define BUS_DEFINE_SET_TRANSIENT_TO_STRING(function, bus_type, type, cast_type, fmt, to_string) \
+ int bus_set_transient_##function( \
+ Unit *u, \
+ const char *name, \
+ cast_type *p, \
+ sd_bus_message *message, \
+ UnitWriteFlags flags, \
+ sd_bus_error *error) { \
+ \
+ const char *s; \
+ type v; \
+ int r; \
+ \
+ assert(p); \
+ \
+ r = sd_bus_message_read(message, bus_type, &v); \
+ if (r < 0) \
+ return r; \
+ \
+ s = to_string(v); \
+ if (!s) \
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \
+ "Invalid %s setting: " fmt, name, v); \
+ \
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
+ *p = (cast_type) v; \
+ unit_write_settingf(u, flags, name, \
+ "%s=%s", name, s); \
+ } \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(function, bus_type, type, cast_type, fmt, to_string) \
+ int bus_set_transient_##function( \
+ Unit *u, \
+ const char *name, \
+ cast_type *p, \
+ sd_bus_message *message, \
+ UnitWriteFlags flags, \
+ sd_bus_error *error) { \
+ \
+ _cleanup_free_ char *s = NULL; \
+ type v; \
+ int r; \
+ \
+ assert(p); \
+ \
+ r = sd_bus_message_read(message, bus_type, &v); \
+ if (r < 0) \
+ return r; \
+ \
+ r = to_string(v, &s); \
+ if (r == -EINVAL) \
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \
+ "Invalid %s setting: " fmt, name, v); \
+ if (r < 0) \
+ return r; \
+ \
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
+ *p = (cast_type) v; \
+ unit_write_settingf(u, flags, name, \
+ "%s=%s", name, s); \
+ } \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define BUS_DEFINE_SET_TRANSIENT_PARSE(function, type, parse) \
+ int bus_set_transient_##function( \
+ Unit *u, \
+ const char *name, \
+ type *p, \
+ sd_bus_message *message, \
+ UnitWriteFlags flags, \
+ sd_bus_error *error) { \
+ \
+ const char *s; \
+ type v; \
+ int r; \
+ \
+ assert(p); \
+ \
+ r = sd_bus_message_read(message, "s", &s); \
+ if (r < 0) \
+ return r; \
+ \
+ v = parse(s); \
+ if (v < 0) \
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \
+ "Invalid %s setting: %s", name, s); \
+ \
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
+ *p = v; \
+ unit_write_settingf(u, flags, name, \
+ "%s=%s", name, s); \
+ } \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(function, type, parse) \
+ int bus_set_transient_##function( \
+ Unit *u, \
+ const char *name, \
+ type *p, \
+ sd_bus_message *message, \
+ UnitWriteFlags flags, \
+ sd_bus_error *error) { \
+ \
+ const char *s; \
+ type v; \
+ int r; \
+ \
+ assert(p); \
+ \
+ r = sd_bus_message_read(message, "s", &s); \
+ if (r < 0) \
+ return r; \
+ \
+ r = parse(s, &v); \
+ if (r < 0) \
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \
+ "Invalid %s setting: %s", name, s); \
+ \
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
+ *p = v; \
+ unit_write_settingf(u, flags, name, \
+ "%s=%s", name, strempty(s)); \
+ } \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(function, check) \
+ int bus_set_transient_##function( \
+ Unit *u, \
+ const char *name, \
+ char **p, \
+ sd_bus_message *message, \
+ UnitWriteFlags flags, \
+ sd_bus_error *error) { \
+ \
+ const char *v; \
+ int r; \
+ \
+ assert(p); \
+ \
+ r = sd_bus_message_read(message, "s", &v); \
+ if (r < 0) \
+ return r; \
+ \
+ if (!isempty(v) && !check(v)) \
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \
+ "Invalid %s setting: %s", name, v); \
+ \
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
+ r = free_and_strdup(p, empty_to_null(v)); \
+ if (r < 0) \
+ return r; \
+ \
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, \
+ "%s=%s", name, strempty(v)); \
+ } \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define BUS_DEFINE_SET_CGROUP_WEIGHT(function, mask, check, val, str) \
+ int bus_cgroup_set_##function( \
+ Unit *u, \
+ const char *name, \
+ uint64_t *p, \
+ sd_bus_message *message, \
+ UnitWriteFlags flags, \
+ sd_bus_error *error) { \
+ \
+ uint64_t v; \
+ int r; \
+ \
+ assert(p); \
+ \
+ r = sd_bus_message_read(message, "t", &v); \
+ if (r < 0) \
+ return r; \
+ \
+ if (!check(v)) \
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \
+ "Value specified in %s is out of range", name); \
+ \
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
+ *p = v; \
+ unit_invalidate_cgroup(u, (mask)); \
+ \
+ if (v == (val)) \
+ unit_write_settingf(u, flags, name, \
+ "%s=" str, name); \
+ else \
+ unit_write_settingf(u, flags, name, \
+ "%s=%" PRIu64, name, v); \
+ } \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define BUS_DEFINE_SET_CGROUP_SCALE(function, mask, scale) \
+ int bus_cgroup_set_##function##_scale( \
+ Unit *u, \
+ const char *name, \
+ uint64_t *p, \
+ sd_bus_message *message, \
+ UnitWriteFlags flags, \
+ sd_bus_error *error) { \
+ \
+ uint64_t v; \
+ uint32_t raw; \
+ int r; \
+ \
+ assert(p); \
+ \
+ r = sd_bus_message_read(message, "u", &raw); \
+ if (r < 0) \
+ return r; \
+ \
+ v = scale(raw, UINT32_MAX); \
+ if (v <= 0 || v >= UINT64_MAX) \
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \
+ "Value specified in %s is out of range", name); \
+ \
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
+ const char *e; \
+ \
+ *p = v; \
+ unit_invalidate_cgroup(u, (mask)); \
+ \
+ /* Chop off suffix */ \
+ assert_se(e = endswith(name, "Scale")); \
+ name = strndupa(name, e - name); \
+ \
+ unit_write_settingf(u, flags, name, "%s=%" PRIu32 "%%", name, \
+ (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); \
+ } \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+int bus_set_transient_mode_t(Unit *u, const char *name, mode_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
+int bus_set_transient_unsigned(Unit *u, const char *name, unsigned *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
+int bus_set_transient_user(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
+int bus_set_transient_path(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
+int bus_set_transient_string(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
+int bus_set_transient_bool(Unit *u, const char *name, bool *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
+int bus_set_transient_usec_internal(Unit *u, const char *name, usec_t *p, bool fix_0, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
+static inline int bus_set_transient_usec(Unit *u, const char *name, usec_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error) {
+ return bus_set_transient_usec_internal(u, name, p, false, message, flags, error);
+}
+static inline int bus_set_transient_usec_fix_0(Unit *u, const char *name, usec_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error) {
+ return bus_set_transient_usec_internal(u, name, p, true, message, flags, error);
+}
diff --git a/src/core/dbus.c b/src/core/dbus.c
index b7d8af9396..1c3fca353a 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -37,9 +37,11 @@
#include "dbus-unit.h"
#include "dbus.h"
#include "fd-util.h"
+#include "fs-util.h"
#include "log.h"
#include "missing.h"
#include "mkdir.h"
+#include "process-util.h"
#include "selinux-access.h"
#include "special.h"
#include "string-util.h"
@@ -603,18 +605,16 @@ static int bus_setup_disconnected_match(Manager *m, sd_bus *bus) {
assert(m);
assert(bus);
- r = sd_bus_add_match(
+ r = sd_bus_match_signal_async(
bus,
NULL,
- "sender='org.freedesktop.DBus.Local',"
- "type='signal',"
- "path='/org/freedesktop/DBus/Local',"
- "interface='org.freedesktop.DBus.Local',"
- "member='Disconnected'",
- signal_disconnected, m);
-
+ "org.freedesktop.DBus.Local",
+ "/org/freedesktop/DBus/Local",
+ "org.freedesktop.DBus.Local",
+ "Disconnected",
+ signal_disconnected, NULL, m);
if (r < 0)
- return log_error_errno(r, "Failed to register match for Disconnected message: %m");
+ return log_error_errno(r, "Failed to request match for Disconnected message: %m");
return 0;
}
@@ -683,6 +683,12 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
+ r = sd_bus_set_sender(bus, "org.freedesktop.systemd1");
+ if (r < 0) {
+ log_warning_errno(r, "Failed to set direct connection sender: %m");
+ return 0;
+ }
+
r = sd_bus_start(bus);
if (r < 0) {
log_warning_errno(r, "Failed to start new connection bus: %m");
@@ -813,26 +819,23 @@ static int bus_setup_api(Manager *m, sd_bus *bus) {
log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name);
}
- r = sd_bus_add_match(
+ r = sd_bus_match_signal_async(
bus,
NULL,
- "type='signal',"
- "sender='org.freedesktop.DBus',"
- "path='/org/freedesktop/DBus',"
- "interface='org.freedesktop.systemd1.Activator',"
- "member='ActivationRequest'",
- signal_activation_request, m);
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.systemd1.Activator",
+ "ActivationRequest",
+ signal_activation_request, NULL, m);
if (r < 0)
log_warning_errno(r, "Failed to subscribe to activation signal: %m");
- /* Allow replacing of our name, to ease implementation of
- * reexecution, where we keep the old connection open until
- * after the new connection is set up and the name installed
- * to allow clients to synchronously wait for reexecution to
- * finish */
- r = sd_bus_request_name(bus,"org.freedesktop.systemd1", SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_ALLOW_REPLACEMENT);
+ /* Allow replacing of our name, to ease implementation of reexecution, where we keep the old connection open
+ * until after the new connection is set up and the name installed to allow clients to synchronously wait for
+ * reexecution to finish */
+ r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.systemd1", SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_ALLOW_REPLACEMENT, NULL, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to register name: %m");
+ return log_error_errno(r, "Failed to request name: %m");
r = manager_sync_bus_names(m, bus);
if (r < 0)
@@ -857,28 +860,21 @@ static int bus_init_api(Manager *m) {
r = sd_bus_open_system(&bus);
else
r = sd_bus_open_user(&bus);
-
- if (r < 0) {
- log_debug("Failed to connect to API bus, retrying later...");
- return 0;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to API bus: %m");
r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL);
- if (r < 0) {
- log_error_errno(r, "Failed to attach API bus to event loop: %m");
- return 0;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach API bus to event loop: %m");
r = bus_setup_disconnected_match(m, bus);
if (r < 0)
- return 0;
+ return r;
}
r = bus_setup_api(m, bus);
- if (r < 0) {
- log_error_errno(r, "Failed to set up API bus: %m");
- return 0;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up API bus: %m");
m->api_bus = bus;
bus = NULL;
@@ -894,16 +890,16 @@ static int bus_setup_system(Manager *m, sd_bus *bus) {
/* if we are a user instance we get the Released message via the system bus */
if (MANAGER_IS_USER(m)) {
- r = sd_bus_add_match(
+ r = sd_bus_match_signal_async(
bus,
NULL,
- "type='signal',"
- "interface='org.freedesktop.systemd1.Agent',"
- "member='Released',"
- "path='/org/freedesktop/systemd1/agent'",
- signal_agent_released, m);
+ NULL,
+ "/org/freedesktop/systemd1/agent",
+ "org.freedesktop.systemd1.Agent",
+ "Released",
+ signal_agent_released, NULL, m);
if (r < 0)
- log_warning_errno(r, "Failed to register Released match on system bus: %m");
+ log_warning_errno(r, "Failed to request Released match on system bus: %m");
}
log_debug("Successfully connected to system bus.");
@@ -924,26 +920,20 @@ static int bus_init_system(Manager *m) {
}
r = sd_bus_open_system(&bus);
- if (r < 0) {
- log_debug("Failed to connect to system bus, retrying later...");
- return 0;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to system bus: %m");
r = bus_setup_disconnected_match(m, bus);
if (r < 0)
- return 0;
+ return r;
r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL);
- if (r < 0) {
- log_error_errno(r, "Failed to attach system bus to event loop: %m");
- return 0;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach system bus to event loop: %m");
r = bus_setup_system(m, bus);
- if (r < 0) {
- log_error_errno(r, "Failed to set up system bus: %m");
- return 0;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up system bus: %m");
m->system_bus = bus;
bus = NULL;
@@ -1005,6 +995,9 @@ static int bus_init_private(Manager *m) {
if (r < 0)
return log_error_errno(errno, "Failed to make private socket listening: %m");
+ /* Generate an inotify event in case somebody waits for this socket to appear using inotify() */
+ (void) touch(sa.un.sun_path);
+
r = sd_event_add_io(m->event, &s, fd, EPOLLIN, bus_on_connection, m);
if (r < 0)
return log_error_errno(r, "Failed to allocate event source: %m");
@@ -1026,16 +1019,16 @@ int bus_init(Manager *m, bool try_bus_connect) {
if (try_bus_connect) {
r = bus_init_system(m);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to initialize D-Bus connection: %m");
r = bus_init_api(m);
if (r < 0)
- return r;
+ return log_error_errno(r, "Error occured during D-Bus APIs initialization: %m");
}
r = bus_init_private(m);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to create private D-Bus server: %m");
return 0;
}
diff --git a/src/core/device.c b/src/core/device.c
index dec6e74f95..a43664d3bd 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -796,14 +796,12 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
}
if (streq(action, "change")) {
- _cleanup_free_ char *e = NULL;
Unit *u;
+ Device *d, *l, *n;
- r = unit_name_from_path(sysfs, ".device", &e);
- if (r < 0)
- log_error_errno(r, "Failed to generate unit name from device path: %m");
- else {
- u = manager_get_unit(m, e);
+ l = hashmap_get(m->devices_by_sysfs, sysfs);
+ LIST_FOREACH_SAFE(same_sysfs, d, n, l) {
+ u = &d->meta;
if (u && UNIT_VTABLE(u)->active_state(u) == UNIT_ACTIVE) {
r = manager_propagate_reload(m, u, JOB_REPLACE, NULL);
if (r < 0)
diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c
index 308608e426..decfacd600 100644
--- a/src/core/emergency-action.c
+++ b/src/core/emergency-action.c
@@ -49,6 +49,11 @@ int emergency_action(
if (action == EMERGENCY_ACTION_NONE)
return -ECANCELED;
+ if (!m->service_watchdogs) {
+ log_warning("Watchdog disabled! Not acting on: %s", reason);
+ return -ECANCELED;
+ }
+
if (!MANAGER_IS_SYSTEM(m)) {
/* Downgrade all options to simply exiting if we run
* in user mode */
diff --git a/src/core/execute.c b/src/core/execute.c
index f20246796f..0df3971df6 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -1207,27 +1207,19 @@ static int setup_pam(
parent_pid = getpid_cached();
- pam_pid = fork();
- if (pam_pid < 0) {
- r = -errno;
+ r = safe_fork("(sd-pam)", 0, &pam_pid);
+ if (r < 0)
goto fail;
- }
-
- if (pam_pid == 0) {
+ if (r == 0) {
int sig, ret = EXIT_PAM;
/* The child's job is to reset the PAM session on
* termination */
barrier_set_role(&barrier, BARRIER_CHILD);
- /* This string must fit in 10 chars (i.e. the length
- * of "/sbin/init"), to look pretty in /bin/ps */
- rename_process("(sd-pam)");
-
- /* Make sure we don't keep open the passed fds in this
- child. We assume that otherwise only those fds are
- open here that have been opened by PAM. */
- close_many(fds, n_fds);
+ /* Make sure we don't keep open the passed fds in this child. We assume that otherwise only those fds
+ * are open here that have been opened by PAM. */
+ (void) close_many(fds, n_fds);
/* Drop privileges - we don't need any to pam_close_session
* and this will make PR_SET_PDEATHSIG work in most cases.
@@ -1784,7 +1776,7 @@ static int build_pass_environment(const ExecContext *c, char ***ret) {
static bool exec_needs_mount_namespace(
const ExecContext *context,
const ExecParameters *params,
- ExecRuntime *runtime) {
+ const ExecRuntime *runtime) {
assert(context);
assert(params);
@@ -1797,12 +1789,7 @@ static bool exec_needs_mount_namespace(
!strv_isempty(context->inaccessible_paths))
return true;
- if (context->n_bind_mounts > 0 ||
- !strv_isempty(context->directories[EXEC_DIRECTORY_RUNTIME].paths) ||
- !strv_isempty(context->directories[EXEC_DIRECTORY_STATE].paths) ||
- !strv_isempty(context->directories[EXEC_DIRECTORY_CACHE].paths) ||
- !strv_isempty(context->directories[EXEC_DIRECTORY_LOGS].paths) ||
- !strv_isempty(context->directories[EXEC_DIRECTORY_CONFIGURATION].paths))
+ if (context->n_bind_mounts > 0)
return true;
if (context->mount_flags != 0)
@@ -1822,6 +1809,12 @@ static bool exec_needs_mount_namespace(
if (context->mount_apivfs && (context->root_image || context->root_directory))
return true;
+ if (context->dynamic_user &&
+ (!strv_isempty(context->directories[EXEC_DIRECTORY_STATE].paths) ||
+ !strv_isempty(context->directories[EXEC_DIRECTORY_CACHE].paths) ||
+ !strv_isempty(context->directories[EXEC_DIRECTORY_LOGS].paths)))
+ return true;
+
return false;
}
@@ -1831,7 +1824,6 @@ static int setup_private_users(uid_t uid, gid_t gid) {
_cleanup_close_ int unshare_ready_fd = -1;
_cleanup_(sigkill_waitp) pid_t pid = 0;
uint64_t c = 1;
- siginfo_t si;
ssize_t n;
int r;
@@ -1879,11 +1871,10 @@ static int setup_private_users(uid_t uid, gid_t gid) {
if (pipe2(errno_pipe, O_CLOEXEC) < 0)
return -errno;
- pid = fork();
- if (pid < 0)
- return -errno;
-
- if (pid == 0) {
+ r = safe_fork("(sd-userns)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
_cleanup_close_ int fd = -1;
const char *a;
pid_t ppid;
@@ -1972,13 +1963,11 @@ static int setup_private_users(uid_t uid, gid_t gid) {
if (n != 0) /* on success we should have read 0 bytes */
return -EIO;
- r = wait_for_terminate(pid, &si);
+ r = wait_for_terminate_and_check("(sd-userns)", pid, 0);
+ pid = 0;
if (r < 0)
return r;
- pid = 0;
-
- /* If something strange happened with the child, let's consider this fatal, too */
- if (si.si_code != CLD_EXITED || si.si_status != 0)
+ if (r != EXIT_SUCCESS) /* If something strange happened with the child, let's consider this fatal, too */
return -EIO;
return 0;
@@ -3418,7 +3407,7 @@ static int exec_child(
return log_oom();
}
- if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
+ if (DEBUG_LOGGING) {
_cleanup_free_ char *line;
line = exec_command_line(final_argv);
@@ -4162,19 +4151,19 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
if (c->pam_name)
fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
- if (strv_length(c->read_write_paths) > 0) {
+ if (!strv_isempty(c->read_write_paths)) {
fprintf(f, "%sReadWritePaths:", prefix);
strv_fprintf(f, c->read_write_paths);
fputs("\n", f);
}
- if (strv_length(c->read_only_paths) > 0) {
+ if (!strv_isempty(c->read_only_paths)) {
fprintf(f, "%sReadOnlyPaths:", prefix);
strv_fprintf(f, c->read_only_paths);
fputs("\n", f);
}
- if (strv_length(c->inaccessible_paths) > 0) {
+ if (!strv_isempty(c->inaccessible_paths)) {
fprintf(f, "%sInaccessiblePaths:", prefix);
strv_fprintf(f, c->inaccessible_paths);
fputs("\n", f);
diff --git a/src/core/ip-address-access.c b/src/core/ip-address-access.c
index 8d72fc03bf..08bd4c0bce 100644
--- a/src/core/ip-address-access.c
+++ b/src/core/ip-address-access.c
@@ -210,13 +210,12 @@ IPAddressAccessItem* ip_address_access_reduce(IPAddressAccessItem *first) {
&b->address,
b->prefixlen,
&a->address);
- if (r <= 0)
- continue;
-
- /* b covers a fully, then let's drop a */
-
- LIST_REMOVE(items, first, a);
- free(a);
+ if (r > 0) {
+ /* b covers a fully, then let's drop a */
+ LIST_REMOVE(items, first, a);
+ free(a);
+ break;
+ }
}
}
diff --git a/src/core/job.c b/src/core/job.c
index 2ea7834dfd..c6de8d27e4 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -306,8 +306,7 @@ void job_dump(Job *j, FILE*f, const char *prefix) {
assert(j);
assert(f);
- if (!prefix)
- prefix = "";
+ prefix = strempty(prefix);
fprintf(f,
"%s-> Job %u:\n"
@@ -1244,7 +1243,7 @@ void job_shutdown_magic(Job *j) {
if (detect_container() > 0)
return;
- asynchronous_sync();
+ (void) asynchronous_sync(NULL);
}
int job_get_timeout(Job *j, usec_t *timeout) {
diff --git a/src/core/kill.c b/src/core/kill.c
index f438c4d8fa..5dfcb780fa 100644
--- a/src/core/kill.c
+++ b/src/core/kill.c
@@ -34,8 +34,7 @@ void kill_context_init(KillContext *c) {
void kill_context_dump(KillContext *c, FILE *f, const char *prefix) {
assert(c);
- if (!prefix)
- prefix = "";
+ prefix = strempty(prefix);
fprintf(f,
"%sKillMode: %s\n"
diff --git a/src/core/killall.c b/src/core/killall.c
index e77763e161..daa9c4ea20 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -89,7 +89,7 @@ static bool ignore_proc(pid_t pid, bool warn_rootfs) {
return true;
}
-static void wait_for_children(Set *pids, sigset_t *mask) {
+static void wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
usec_t until;
assert(mask);
@@ -97,7 +97,7 @@ static void wait_for_children(Set *pids, sigset_t *mask) {
if (set_isempty(pids))
return;
- until = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC;
+ until = now(CLOCK_MONOTONIC) + timeout;
for (;;) {
struct timespec ts;
int k;
@@ -221,7 +221,7 @@ static int killall(int sig, Set *pids, bool send_sighup) {
return set_size(pids);
}
-void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup) {
+void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout) {
sigset_t mask, oldmask;
_cleanup_set_free_ Set *pids = NULL;
@@ -241,7 +241,7 @@ void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup) {
log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m");
if (wait_for_exit)
- wait_for_children(pids, &mask);
+ wait_for_children(pids, &mask, timeout);
assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
}
diff --git a/src/core/killall.h b/src/core/killall.h
index 01bd6e52b3..45e97ab594 100644
--- a/src/core/killall.h
+++ b/src/core/killall.h
@@ -20,4 +20,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup);
+#include "time-util.h"
+
+void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 240f331778..dde5010e02 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -214,6 +214,7 @@ Unit.RefuseManualStop, config_parse_bool, 0,
Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate)
Unit.DefaultDependencies, config_parse_bool, 0, offsetof(Unit, default_dependencies)
Unit.OnFailureJobMode, config_parse_job_mode, 0, offsetof(Unit, on_failure_job_mode)
+m4_dnl The following is a legacy alias name for compatibility
Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode)
Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate)
Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LEGACY, 0
@@ -241,6 +242,7 @@ Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_F
Unit.ConditionNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, conditions)
Unit.ConditionFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, conditions)
Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions)
+Unit.ConditionKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, conditions)
Unit.ConditionArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, conditions)
Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, conditions)
Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, conditions)
@@ -249,6 +251,7 @@ Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_H
Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, conditions)
Unit.ConditionUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, conditions)
Unit.ConditionGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, conditions)
+Unit.ConditionControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, conditions)
Unit.ConditionNull, config_parse_unit_condition_null, 0, offsetof(Unit, conditions)
Unit.AssertPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, asserts)
Unit.AssertPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, asserts)
@@ -262,6 +265,7 @@ Unit.AssertFileIsExecutable, config_parse_unit_condition_path, CONDITION_F
Unit.AssertNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, asserts)
Unit.AssertFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, asserts)
Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts)
+Unit.AssertKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, asserts)
Unit.AssertArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, asserts)
Unit.AssertVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, asserts)
Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, asserts)
@@ -270,6 +274,7 @@ Unit.AssertHost, config_parse_unit_condition_string, CONDITION_H
Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, asserts)
Unit.AssertUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, asserts)
Unit.AssertGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, asserts)
+Unit.AssertControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, asserts)
Unit.AssertNull, config_parse_unit_condition_null, 0, offsetof(Unit, asserts)
Unit.CollectMode, config_parse_collect_mode, 0, offsetof(Unit, collect_mode)
m4_dnl
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index d6c616502b..00408c4b84 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -64,6 +64,7 @@
#include "securebits.h"
#include "securebits-util.h"
#include "signal-util.h"
+#include "socket-protocol-list.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
@@ -433,11 +434,9 @@ int config_parse_socket_listen(const char *unit,
p->n_auxiliary_fds = 0;
p->socket = s;
- if (s->ports) {
- LIST_FIND_TAIL(port, s->ports, tail);
- LIST_INSERT_AFTER(port, s->ports, tail, p);
- } else
- LIST_PREPEND(port, s->ports, p);
+ LIST_FIND_TAIL(port, s->ports, tail);
+ LIST_INSERT_AFTER(port, s->ports, tail, p);
+
p = NULL;
return 0;
@@ -454,6 +453,7 @@ int config_parse_socket_protocol(const char *unit,
void *data,
void *userdata) {
Socket *s;
+ int r;
assert(filename);
assert(lvalue);
@@ -462,15 +462,17 @@ int config_parse_socket_protocol(const char *unit,
s = SOCKET(data);
- if (streq(rvalue, "udplite"))
- s->socket_protocol = IPPROTO_UDPLITE;
- else if (streq(rvalue, "sctp"))
- s->socket_protocol = IPPROTO_SCTP;
- else {
+ r = socket_protocol_from_name(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid socket protocol, ignoring: %s", rvalue);
+ return 0;
+ } else if (!IN_SET(r, IPPROTO_UDPLITE, IPPROTO_SCTP)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Socket protocol not supported, ignoring: %s", rvalue);
return 0;
}
+ s->socket_protocol = r;
+
return 0;
}
@@ -495,19 +497,13 @@ int config_parse_socket_bind(const char *unit,
s = SOCKET(data);
- b = socket_address_bind_ipv6_only_from_string(rvalue);
+ b = parse_socket_address_bind_ipv6_only_or_bool(rvalue);
if (b < 0) {
- int r;
-
- r = parse_boolean(rvalue);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
- return 0;
- }
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
+ return 0;
+ }
- s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
- } else
- s->bind_ipv6_only = b;
+ s->bind_ipv6_only = b;
return 0;
}
@@ -2891,60 +2887,6 @@ int config_parse_documentation(const char *unit,
}
#if HAVE_SECCOMP
-
-static int syscall_filter_parse_one(
- const char *unit,
- const char *filename,
- unsigned line,
- ExecContext *c,
- bool invert,
- const char *t,
- bool warn,
- int errno_num) {
- int r;
-
- if (t[0] == '@') {
- const SyscallFilterSet *set;
- const char *i;
-
- set = syscall_filter_set_find(t);
- if (!set) {
- if (warn)
- log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown system call group, ignoring: %s", t);
- return 0;
- }
-
- NULSTR_FOREACH(i, set->value) {
- r = syscall_filter_parse_one(unit, filename, line, c, invert, i, false, errno_num);
- if (r < 0)
- return r;
- }
- } else {
- int id;
-
- id = seccomp_syscall_resolve_name(t);
- if (id == __NR_SCMP_ERROR) {
- if (warn)
- log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse system call, ignoring: %s", t);
- return 0;
- }
-
- /* If we previously wanted to forbid a syscall and now
- * we want to allow it, then remove it from the list.
- */
- if (!invert == c->syscall_whitelist) {
- r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num));
- if (r == 0)
- return 0;
- if (r < 0)
- return log_oom();
- } else
- (void) hashmap_remove(c->syscall_filter, INT_TO_PTR(id + 1));
- }
-
- return 0;
-}
-
int config_parse_syscall_filter(
const char *unit,
const char *filename,
@@ -2993,7 +2935,7 @@ int config_parse_syscall_filter(
c->syscall_whitelist = true;
/* Accept default syscalls if we are on a whitelist */
- r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false, -1);
+ r = seccomp_parse_syscall_filter(false, "@default", -1, c->syscall_filter, true);
if (r < 0)
return r;
}
@@ -3020,7 +2962,7 @@ int config_parse_syscall_filter(
continue;
}
- r = syscall_filter_parse_one(unit, filename, line, c, invert, name, true, num);
+ r = seccomp_parse_syscall_filter_and_warn(invert, name, num, c->syscall_filter, c->syscall_whitelist, unit, filename, line);
if (r < 0)
return r;
}
@@ -3575,7 +3517,8 @@ int config_parse_device_allow(
if (!path)
return log_oom();
- if (!is_deviceallow_pattern(path)) {
+ if (!is_deviceallow_pattern(path) &&
+ !path_startswith(path, "/run/systemd/inaccessible/")) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
return 0;
}
@@ -3675,7 +3618,8 @@ int config_parse_io_device_weight(
if (!path)
return log_oom();
- if (!path_startswith(path, "/dev")) {
+ if (!path_startswith(path, "/dev") &&
+ !path_startswith(path, "/run/systemd/inaccessible/")) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
return 0;
}
@@ -3748,7 +3692,8 @@ int config_parse_io_limit(
if (!path)
return log_oom();
- if (!path_startswith(path, "/dev")) {
+ if (!path_startswith(path, "/dev") &&
+ !path_startswith(path, "/run/systemd/inaccessible/")) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
return 0;
}
@@ -3862,7 +3807,8 @@ int config_parse_blockio_device_weight(
if (!path)
return log_oom();
- if (!path_startswith(path, "/dev")) {
+ if (!path_startswith(path, "/dev") &&
+ !path_startswith(path, "/run/systemd/inaccessible/")) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
return 0;
}
@@ -3936,7 +3882,8 @@ int config_parse_blockio_bandwidth(
if (!path)
return log_oom();
- if (!path_startswith(path, "/dev")) {
+ if (!path_startswith(path, "/dev") &&
+ !path_startswith(path, "/run/systemd/inaccessible/")) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
return 0;
}
@@ -4002,6 +3949,8 @@ int config_parse_job_mode_isolate(
return 0;
}
+ log_notice("%s is deprecated. Please use OnFailureJobMode= instead", lvalue);
+
*m = r ? JOB_ISOLATE : JOB_REPLACE;
return 0;
}
@@ -4412,7 +4361,7 @@ int config_parse_protect_home(
void *userdata) {
ExecContext *c = data;
- int k;
+ ProtectHome h;
assert(filename);
assert(lvalue);
@@ -4422,23 +4371,14 @@ int config_parse_protect_home(
/* Our enum shall be a superset of booleans, hence first try
* to parse as boolean, and then as enum */
- k = parse_boolean(rvalue);
- if (k > 0)
- c->protect_home = PROTECT_HOME_YES;
- else if (k == 0)
- c->protect_home = PROTECT_HOME_NO;
- else {
- ProtectHome h;
-
- h = protect_home_from_string(rvalue);
- if (h < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
- return 0;
- }
-
- c->protect_home = h;
+ h = parse_protect_home_or_bool(rvalue);
+ if (h < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
+ return 0;
}
+ c->protect_home = h;
+
return 0;
}
@@ -4455,7 +4395,7 @@ int config_parse_protect_system(
void *userdata) {
ExecContext *c = data;
- int k;
+ ProtectSystem s;
assert(filename);
assert(lvalue);
@@ -4465,23 +4405,14 @@ int config_parse_protect_system(
/* Our enum shall be a superset of booleans, hence first try
* to parse as boolean, and then as enum */
- k = parse_boolean(rvalue);
- if (k > 0)
- c->protect_system = PROTECT_SYSTEM_YES;
- else if (k == 0)
- c->protect_system = PROTECT_SYSTEM_NO;
- else {
- ProtectSystem s;
-
- s = protect_system_from_string(rvalue);
- if (s < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
- return 0;
- }
-
- c->protect_system = s;
+ s = parse_protect_system_or_bool(rvalue);
+ if (s < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
+ return 0;
}
+ c->protect_system = s;
+
return 0;
}
diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c
index 6240a83197..0c43cf2418 100644
--- a/src/core/locale-setup.c
+++ b/src/core/locale-setup.c
@@ -20,6 +20,7 @@
#include <errno.h>
#include <stdlib.h>
+#include <string.h>
#include "env-util.h"
#include "fileio.h"
diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c
index 9a75525894..1528034e81 100644
--- a/src/core/loopback-setup.c
+++ b/src/core/loopback-setup.c
@@ -32,21 +32,27 @@
struct state {
unsigned n_messages;
int rcode;
- const char *title;
+ const char *error_message;
+ const char *success_message;
};
static int generic_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
struct state *s = userdata;
+ int r;
assert(s);
assert(s->n_messages > 0);
s->n_messages--;
errno = 0;
- log_debug_errno(sd_netlink_message_get_errno(m), "Failed to %s: %m", s->title);
- s->rcode = sd_netlink_message_get_errno(m);
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0)
+ log_debug_errno(r, "%s: %m", s->error_message);
+ else
+ log_debug("%s", s->success_message);
+ s->rcode = r;
return 0;
}
@@ -165,9 +171,16 @@ static bool check_loopback(sd_netlink *rtnl) {
int loopback_setup(void) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
- struct state state_4 = { .title = "add address 127.0.0.1 to loopback interface" },
- state_6 = { .title = "add address ::1 to loopback interface"},
- state_up = { .title = "bring loopback interface up" };
+ struct state state_4 = {
+ .error_message = "Failed to add address 127.0.0.1 to loopback interface",
+ .success_message = "Successfully added address 127.0.0.1 to loopback interface",
+ }, state_6 = {
+ .error_message = "Failed to add address ::1 to loopback interface",
+ .success_message = "Successfully added address ::1 to loopback interface",
+ }, state_up = {
+ .error_message = "Failed to bring loopback interface up",
+ .success_message = "Successfully brought loopback interface up",
+ };
int r;
r = sd_netlink_open(&rtnl);
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index 767e97206c..1b7424800c 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -119,16 +119,15 @@ int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) {
fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
if (old_errno == EROFS && errno == ENOENT)
- log_error_errno(errno,
+ return log_error_errno(errno,
"System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n"
"Booting up is supported only when:\n"
"1) /etc/machine-id exists and is populated.\n"
"2) /etc/machine-id exists and is empty.\n"
"3) /etc/machine-id is missing and /etc is writable.\n");
else
- log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
-
- return -errno;
+ return log_error_errno(errno,
+ "Cannot open %s: %m", etc_machine_id);
}
writable = false;
diff --git a/src/core/main.c b/src/core/main.c
index 2ad5073368..56200a8fad 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -91,6 +91,7 @@
#include "terminal-util.h"
#include "umask-util.h"
#include "user-util.h"
+#include "util.h"
#include "virt.h"
#include "watchdog.h"
@@ -111,6 +112,7 @@ static char *arg_confirm_spawn = NULL;
static ShowStatus arg_show_status = _SHOW_STATUS_UNSET;
static bool arg_switched_root = false;
static bool arg_no_pager = false;
+static bool arg_service_watchdogs = true;
static char ***arg_join_controllers = NULL;
static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL;
static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT;
@@ -395,6 +397,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
arg_confirm_spawn = s;
}
+ } else if (proc_cmdline_key_streq(key, "systemd.service_watchdogs")) {
+
+ r = value ? parse_boolean(value) : true;
+ if (r < 0)
+ log_warning("Failed to parse service watchdog switch %s. Ignoring.", value);
+ else
+ arg_service_watchdogs = r;
+
} else if (proc_cmdline_key_streq(key, "systemd.show_status")) {
if (value) {
@@ -854,6 +864,7 @@ static void set_manager_settings(Manager *m) {
assert(m);
m->confirm_spawn = arg_confirm_spawn;
+ m->service_watchdogs = arg_service_watchdogs;
m->runtime_watchdog = arg_runtime_watchdog;
m->shutdown_watchdog = arg_shutdown_watchdog;
m->cad_burst_action = arg_cad_burst_action;
@@ -885,7 +896,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SWITCHED_ROOT,
ARG_DEFAULT_STD_OUTPUT,
ARG_DEFAULT_STD_ERROR,
- ARG_MACHINE_ID
+ ARG_MACHINE_ID,
+ ARG_SERVICE_WATCHDOGS,
};
static const struct option options[] = {
@@ -912,6 +924,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, },
{ "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, },
{ "machine-id", required_argument, NULL, ARG_MACHINE_ID },
+ { "service-watchdogs", required_argument, NULL, ARG_SERVICE_WATCHDOGS },
{}
};
@@ -1066,6 +1079,13 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(r, "Failed to parse confirm spawn option: %m");
break;
+ case ARG_SERVICE_WATCHDOGS:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse service watchdogs boolean: %s", optarg);
+ arg_service_watchdogs = r;
+ break;
+
case ARG_SHOW_STATUS:
if (optarg) {
r = parse_show_status(optarg, &arg_show_status);
@@ -1336,20 +1356,23 @@ static int enforce_syscall_archs(Set *archs) {
static int status_welcome(void) {
_cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL;
+ const char *fn;
int r;
- r = parse_env_file("/etc/os-release", NEWLINE,
- "PRETTY_NAME", &pretty_name,
- "ANSI_COLOR", &ansi_color,
- NULL);
- if (r == -ENOENT)
- r = parse_env_file("/usr/lib/os-release", NEWLINE,
+ if (arg_show_status <= 0)
+ return 0;
+
+ FOREACH_STRING(fn, "/etc/os-release", "/usr/lib/os-release") {
+ r = parse_env_file(fn, NEWLINE,
"PRETTY_NAME", &pretty_name,
"ANSI_COLOR", &ansi_color,
NULL);
+ if (r != -ENOENT)
+ break;
+ }
if (r < 0 && r != -ENOENT)
- log_warning_errno(r, "Failed to read os-release file: %m");
+ log_warning_errno(r, "Failed to read os-release file, ignoring: %m");
if (log_get_show_color())
return status_printf(NULL, false, false,
@@ -1413,32 +1436,29 @@ static int bump_unix_max_dgram_qlen(void) {
static int fixup_environment(void) {
_cleanup_free_ char *term = NULL;
+ const char *t;
int r;
- /* We expect the environment to be set correctly
- * if run inside a container. */
+ /* Only fix up the environment when we are started as PID 1 */
+ if (getpid_cached() != 1)
+ return 0;
+
+ /* We expect the environment to be set correctly if run inside a container. */
if (detect_container() > 0)
return 0;
- /* When started as PID1, the kernel uses /dev/console
- * for our stdios and uses TERM=linux whatever the
- * backend device used by the console. We try to make
- * a better guess here since some consoles might not
- * have support for color mode for example.
+ /* When started as PID1, the kernel uses /dev/console for our stdios and uses TERM=linux whatever the backend
+ * device used by the console. We try to make a better guess here since some consoles might not have support
+ * for color mode for example.
*
- * However if TERM was configured through the kernel
- * command line then leave it alone. */
-
+ * However if TERM was configured through the kernel command line then leave it alone. */
r = proc_cmdline_get_key("TERM", 0, &term);
if (r < 0)
return r;
- if (r == 0) {
- term = strdup(default_term_for_tty("/dev/console"));
- if (!term)
- return -ENOMEM;
- }
- if (setenv("TERM", term, 1) < 0)
+ t = term ?: default_term_for_tty("/dev/console");
+
+ if (setenv("TERM", t, 1) < 0)
return -errno;
return 0;
@@ -1457,7 +1477,7 @@ static void redirect_telinit(int argc, char *argv[]) {
execv(SYSTEMCTL_BINARY_PATH, argv);
log_error_errno(errno, "Failed to exec " SYSTEMCTL_BINARY_PATH ": %m");
- exit(1);
+ exit(EXIT_FAILURE);
#endif
}
@@ -1466,17 +1486,19 @@ static int become_shutdown(
int retval) {
char log_level[DECIMAL_STR_MAX(int) + 1],
- exit_code[DECIMAL_STR_MAX(uint8_t) + 1];
+ exit_code[DECIMAL_STR_MAX(uint8_t) + 1],
+ timeout[DECIMAL_STR_MAX(usec_t) + 1];
- const char* command_line[11] = {
+ const char* command_line[13] = {
SYSTEMD_SHUTDOWN_BINARY_PATH,
shutdown_verb,
+ "--timeout", timeout,
"--log-level", log_level,
"--log-target",
};
_cleanup_strv_free_ char **env_block = NULL;
- size_t pos = 5;
+ size_t pos = 7;
int r;
assert(shutdown_verb);
@@ -1484,6 +1506,7 @@ static int become_shutdown(
env_block = strv_copy(environ);
xsprintf(log_level, "%d", log_get_max_level());
+ xsprintf(timeout, "%" PRI_USEC "us", arg_default_timeout_stop_usec);
switch (log_get_target()) {
@@ -1603,7 +1626,7 @@ static void initialize_coredump(bool skip_setup) {
/* But at the same time, turn off the core_pattern logic by default, so that no coredumps are stored
* until the systemd-coredump tool is enabled via sysctl. */
if (!skip_setup)
- (void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
+ disable_coredumps();
}
static void do_reexecute(
@@ -1639,7 +1662,7 @@ static void do_reexecute(
if (switch_root_dir) {
/* Kill all remaining processes from the initrd, but don't wait for them, so that we can handle the
* SIGCHLD for them after deserializing. */
- broadcast_signal(SIGTERM, false, true);
+ broadcast_signal(SIGTERM, false, true, arg_default_timeout_stop_usec);
/* And switch root with MS_MOVE, because we remove the old directory afterwards and detach it. */
r = switch_root(switch_root_dir, "/mnt", true, MS_MOVE);
@@ -1889,11 +1912,13 @@ static void log_execution_mode(bool *ret_first_boot) {
log_info("Running with unpopulated /etc.");
}
} else {
- _cleanup_free_ char *t;
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *t;
- t = uid_to_name(getuid());
- log_debug(PACKAGE_STRING " running in %suser mode for user " UID_FMT "/%s. (" SYSTEMD_FEATURES ")",
- arg_action == ACTION_TEST ? " test" : "", getuid(), strna(t));
+ t = uid_to_name(getuid());
+ log_debug(PACKAGE_STRING " running in %suser mode for user " UID_FMT "/%s. (" SYSTEMD_FEATURES ")",
+ arg_action == ACTION_TEST ? " test" : "", getuid(), strna(t));
+ }
*ret_first_boot = false;
}
@@ -1916,31 +1941,42 @@ static int initialize_runtime(
* - Some only apply when we first start up, but not when we reexecute
*/
- if (arg_system && !skip_setup) {
- if (arg_show_status > 0)
+ if (arg_action != ACTION_RUN)
+ return 0;
+
+ if (arg_system) {
+ /* Make sure we leave a core dump without panicing the kernel. */
+ install_crash_handler();
+
+ if (!skip_setup) {
+ r = mount_cgroup_controllers(arg_join_controllers);
+ if (r < 0) {
+ *ret_error_message = "Failed to mount cgroup hierarchies";
+ return r;
+ }
+
status_welcome();
+ hostname_setup();
+ machine_id_setup(NULL, arg_machine_id, NULL);
+ loopback_setup();
+ bump_unix_max_dgram_qlen();
+ test_usr();
+ write_container_id();
+ }
- hostname_setup();
- machine_id_setup(NULL, arg_machine_id, NULL);
- loopback_setup();
- bump_unix_max_dgram_qlen();
- test_usr();
- write_container_id();
- }
+ if (arg_watchdog_device) {
+ r = watchdog_set_device(arg_watchdog_device);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", arg_watchdog_device);
+ }
- if (arg_system && arg_watchdog_device) {
- r = watchdog_set_device(arg_watchdog_device);
- if (r < 0)
- log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m",
- arg_watchdog_device);
+ if (arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY)
+ watchdog_set_timeout(&arg_runtime_watchdog);
}
- if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY)
- watchdog_set_timeout(&arg_runtime_watchdog);
-
if (arg_timer_slack_nsec != NSEC_INFINITY)
if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0)
- log_error_errno(errno, "Failed to adjust timer slack: %m");
+ log_warning_errno(errno, "Failed to adjust timer slack, ignoring: %m");
if (arg_system && !cap_test_all(arg_capability_bounding_set)) {
r = capability_bounding_set_drop_usermode(arg_capability_bounding_set);
@@ -2053,60 +2089,256 @@ static void free_arguments(void) {
arg_syscall_archs = set_free(arg_syscall_archs);
}
+static int load_configuration(int argc, char **argv, const char **ret_error_message) {
+ int r;
+
+ assert(ret_error_message);
+
+ r = initialize_join_controllers();
+ if (r < 0) {
+ *ret_error_message = "Failed to initialize cgroup controller joining table";
+ return r;
+ }
+
+ arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U);
+
+ r = parse_config_file();
+ if (r < 0) {
+ *ret_error_message = "Failed to parse config file";
+ return r;
+ }
+
+ if (arg_system) {
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+ }
+
+ /* Note that this also parses bits from the kernel command line, including "debug". */
+ log_parse_environment();
+
+ r = parse_argv(argc, argv);
+ if (r < 0) {
+ *ret_error_message = "Failed to parse commandline arguments";
+ return r;
+ }
+
+ /* Initialize default unit */
+ if (!arg_default_unit) {
+ arg_default_unit = strdup(SPECIAL_DEFAULT_TARGET);
+ if (!arg_default_unit) {
+ *ret_error_message = "Failed to set default unit";
+ return log_oom();
+ }
+ }
+
+ /* Initialize the show status setting if it hasn't been set explicitly yet */
+ if (arg_show_status == _SHOW_STATUS_UNSET)
+ arg_show_status = SHOW_STATUS_YES;
+
+ return 0;
+}
+
+static int safety_checks(void) {
+
+ if (getpid_cached() == 1 &&
+ arg_action != ACTION_RUN) {
+ log_error("Unsupported execution mode while PID 1.");
+ return -EPERM;
+ }
+
+ if (getpid_cached() == 1 &&
+ !arg_system) {
+ log_error("Can't run --user mode as PID 1.");
+ return -EPERM;
+ }
+
+ if (arg_action == ACTION_RUN &&
+ arg_system &&
+ getpid_cached() != 1) {
+ log_error("Can't run system mode unless PID 1.");
+ return -EPERM;
+ }
+
+ if (arg_action == ACTION_TEST &&
+ geteuid() == 0) {
+ log_error("Don't run test mode as root.");
+ return -EPERM;
+ }
+
+ if (!arg_system &&
+ arg_action == ACTION_RUN &&
+ sd_booted() <= 0) {
+ log_error("Trying to run as user instance, but the system has not been booted with systemd.");
+ return -EOPNOTSUPP;
+ }
+
+ if (!arg_system &&
+ arg_action == ACTION_RUN &&
+ !getenv("XDG_RUNTIME_DIR")) {
+ log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set.");
+ return -EUNATCH;
+ }
+
+ if (arg_system &&
+ arg_action == ACTION_RUN &&
+ running_in_chroot() > 0) {
+ log_error("Cannot be run in a chroot() environment.");
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int initialize_security(
+ bool *loaded_policy,
+ dual_timestamp *security_start_timestamp,
+ dual_timestamp *security_finish_timestamp,
+ const char **ret_error_message) {
+
+ int r;
+
+ assert(loaded_policy);
+ assert(security_start_timestamp);
+ assert(security_finish_timestamp);
+ assert(ret_error_message);
+
+ dual_timestamp_get(security_start_timestamp);
+
+ r = mac_selinux_setup(loaded_policy);
+ if (r < 0) {
+ *ret_error_message = "Failed to load SELinux policy";
+ return r;
+ }
+
+ r = mac_smack_setup(loaded_policy);
+ if (r < 0) {
+ *ret_error_message = "Failed to load SMACK policy";
+ return r;
+ }
+
+ r = ima_setup();
+ if (r < 0) {
+ *ret_error_message = "Failed to load IMA policy";
+ return r;
+ }
+
+ dual_timestamp_get(security_finish_timestamp);
+ return 0;
+}
+
+static void test_summary(Manager *m) {
+ assert(m);
+
+ printf("-> By units:\n");
+ manager_dump_units(m, stdout, "\t");
+
+ printf("-> By jobs:\n");
+ manager_dump_jobs(m, stdout, "\t");
+}
+
+static int collect_fds(FDSet **ret_fds, const char **ret_error_message) {
+ int r;
+
+ assert(ret_fds);
+ assert(ret_error_message);
+
+ r = fdset_new_fill(ret_fds);
+ if (r < 0) {
+ *ret_error_message = "Failed to allocate fd set";
+ return log_emergency_errno(r, "Failed to allocate fd set: %m");
+ }
+
+ fdset_cloexec(*ret_fds, true);
+
+ if (arg_serialization)
+ assert_se(fdset_remove(*ret_fds, fileno(arg_serialization)) >= 0);
+
+ return 0;
+}
+
+static void setup_console_terminal(bool skip_setup) {
+
+ if (!arg_system)
+ return;
+
+ /* Become a session leader if we aren't one yet. */
+ (void) setsid();
+
+ /* If we are init, we connect stdin/stdout/stderr to /dev/null and make sure we don't have a controlling
+ * tty. */
+ (void) release_terminal();
+
+ /* Reset the console, but only if this is really init and we are freshly booted */
+ if (getpid_cached() == 1 && !skip_setup)
+ (void) console_setup();
+}
+
+static bool early_skip_setup_check(int argc, char *argv[]) {
+ bool found_deserialize = false;
+ int i;
+
+ /* Determine if this is a reexecution or normal bootup. We do the full command line parsing much later, so
+ * let's just have a quick peek here. Note that if we have switched root, do all the special setup things
+ * anyway, even if in that case we also do deserialization. */
+
+ for (i = 1; i < argc; i++) {
+
+ if (streq(argv[i], "--switched-root"))
+ return false; /* If we switched root, don't skip the setup. */
+ else if (streq(argv[i], "--deserialize"))
+ found_deserialize = true;
+ }
+
+ return found_deserialize; /* When we are deserializing, then we are reexecuting, hence avoid the extensive setup */
+}
+
int main(int argc, char *argv[]) {
- Manager *m = NULL;
- int r, retval = EXIT_FAILURE;
+
+ dual_timestamp initrd_timestamp = DUAL_TIMESTAMP_NULL, userspace_timestamp = DUAL_TIMESTAMP_NULL, kernel_timestamp = DUAL_TIMESTAMP_NULL,
+ security_start_timestamp = DUAL_TIMESTAMP_NULL, security_finish_timestamp = DUAL_TIMESTAMP_NULL;
+ struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0), saved_rlimit_memlock = RLIMIT_MAKE_CONST((rlim_t) -1);
+ bool skip_setup, loaded_policy = false, queue_default_job = false, first_boot = false, reexecute = false;
+ char *switch_root_dir = NULL, *switch_root_init = NULL;
usec_t before_startup, after_startup;
+ static char systemd[] = "systemd";
char timespan[FORMAT_TIMESPAN_MAX];
+ const char *shutdown_verb = NULL, *error_message = NULL;
+ int r, retval = EXIT_FAILURE;
+ Manager *m = NULL;
FDSet *fds = NULL;
- bool reexecute = false;
- const char *shutdown_verb = NULL;
- dual_timestamp initrd_timestamp = DUAL_TIMESTAMP_NULL;
- dual_timestamp userspace_timestamp = DUAL_TIMESTAMP_NULL;
- dual_timestamp kernel_timestamp = DUAL_TIMESTAMP_NULL;
- dual_timestamp security_start_timestamp = DUAL_TIMESTAMP_NULL;
- dual_timestamp security_finish_timestamp = DUAL_TIMESTAMP_NULL;
- static char systemd[] = "systemd";
- bool skip_setup = false;
- bool loaded_policy = false;
- bool queue_default_job = false;
- bool first_boot = false;
- char *switch_root_dir = NULL, *switch_root_init = NULL;
- struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0), saved_rlimit_memlock = RLIMIT_MAKE_CONST((rlim_t) -1);
- const char *error_message = NULL;
+ /* SysV compatibility: redirect init → telinit */
redirect_telinit(argc, argv);
+ /* Take timestamps early on */
dual_timestamp_from_monotonic(&kernel_timestamp, 0);
dual_timestamp_get(&userspace_timestamp);
- /* Determine if this is a reexecution or normal bootup. We do
- * the full command line parsing much later, so let's just
- * have a quick peek here. */
- if (strv_find(argv+1, "--deserialize"))
- skip_setup = true;
+ /* Figure out whether we need to do initialize the system, or if we already did that because we are
+ * reexecuting */
+ skip_setup = early_skip_setup_check(argc, argv);
- /* If we have switched root, do all the special setup
- * things */
- if (strv_find(argv+1, "--switched-root"))
- skip_setup = false;
-
- /* If we get started via the /sbin/init symlink then we are
- called 'init'. After a subsequent reexecution we are then
- called 'systemd'. That is confusing, hence let's call us
- systemd right-away. */
+ /* If we get started via the /sbin/init symlink then we are called 'init'. After a subsequent reexecution we
+ * are then called 'systemd'. That is confusing, hence let's call us systemd right-away. */
program_invocation_short_name = systemd;
(void) prctl(PR_SET_NAME, systemd);
+ /* Save the original command line */
saved_argv = argv;
saved_argc = argc;
+ /* Make sure that if the user says "syslog" we actually log to the journal. */
log_set_upgrade_syslog_to_journal(true);
if (getpid_cached() == 1) {
/* Disable the umask logic */
umask(0);
+ /* Make sure that at least initially we do not ever log to journald/syslogd, because it might not be activated
+ * yet (even though the log socket for it exists). */
+ log_set_prohibit_ipc(true);
+
/* Always reopen /dev/console when running as PID 1 or one of its pre-execve() children. This is
* important so that we never end up logging to any foreign stderr, for example if we have to log in a
* child process right before execve()'ing the actual binary, at a point in time where socket
@@ -2131,18 +2363,13 @@ int main(int argc, char *argv[]) {
goto finish;
}
- dual_timestamp_get(&security_start_timestamp);
- if (mac_selinux_setup(&loaded_policy) < 0) {
- error_message = "Failed to load SELinux policy";
- goto finish;
- } else if (mac_smack_setup(&loaded_policy) < 0) {
- error_message = "Failed to load SMACK policy";
- goto finish;
- } else if (ima_setup() < 0) {
- error_message = "Failed to load IMA policy";
+ r = initialize_security(
+ &loaded_policy,
+ &security_start_timestamp,
+ &security_finish_timestamp,
+ &error_message);
+ if (r < 0)
goto finish;
- }
- dual_timestamp_get(&security_finish_timestamp);
}
if (mac_selinux_init() < 0) {
@@ -2165,7 +2392,6 @@ int main(int argc, char *argv[]) {
/* Running inside a container, as PID 1 */
arg_system = true;
log_set_target(LOG_TARGET_CONSOLE);
- log_close_console(); /* force reopen of /dev/console */
log_open();
/* For later on, see above... */
@@ -2187,11 +2413,14 @@ int main(int argc, char *argv[]) {
initialize_coredump(skip_setup);
+ r = fixup_environment();
+ if (r < 0) {
+ log_emergency_errno(r, "Failed to fix up PID 1 environment: %m");
+ error_message = "Failed to fix up PID1 environment";
+ goto finish;
+ }
+
if (arg_system) {
- if (fixup_environment() < 0) {
- error_message = "Failed to fix up PID1 environment";
- goto finish;
- }
/* Try to figure out if we can use colors with the console. No
* need to do that for user instances since they never log
@@ -2202,12 +2431,6 @@ int main(int argc, char *argv[]) {
log_warning_errno(r, "Failed to redirect standard streams to /dev/null: %m");
}
- r = initialize_join_controllers();
- if (r < 0) {
- error_message = "Failed to initialize cgroup controllers";
- goto finish;
- }
-
/* Mount /proc, /sys and friends, so that /proc/cmdline and
* /proc/$PID/fd is available. */
if (getpid_cached() == 1) {
@@ -2227,62 +2450,19 @@ int main(int argc, char *argv[]) {
(void) reset_all_signal_handlers();
(void) ignore_signals(SIGNALS_IGNORE, -1);
- arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U);
-
- if (parse_config_file() < 0) {
- error_message = "Failed to parse config file";
- goto finish;
- }
-
- if (arg_system) {
- r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
- if (r < 0)
- log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
- }
-
- /* Note that this also parses bits from the kernel command
- * line, including "debug". */
- log_parse_environment();
-
- if (parse_argv(argc, argv) < 0) {
- error_message = "Failed to parse commandline arguments";
- goto finish;
- }
-
- /* Initialize default unit */
- if (!arg_default_unit) {
- arg_default_unit = strdup(SPECIAL_DEFAULT_TARGET);
- if (!arg_default_unit) {
- r = log_oom();
- error_message = "Failed to set default unit";
- goto finish;
- }
- }
-
- if (arg_action == ACTION_TEST &&
- geteuid() == 0) {
- log_error("Don't run test mode as root.");
- goto finish;
- }
-
- if (!arg_system &&
- arg_action == ACTION_RUN &&
- sd_booted() <= 0) {
- log_error("Trying to run as user instance, but the system has not been booted with systemd.");
+ r = load_configuration(argc, argv, &error_message);
+ if (r < 0)
goto finish;
- }
- if (arg_system &&
- arg_action == ACTION_RUN &&
- running_in_chroot() > 0) {
- log_error("Cannot be run in a chroot() environment.");
+ r = safety_checks();
+ if (r < 0)
goto finish;
- }
- if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP)) {
+ if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP, ACTION_DUMP_CONFIGURATION_ITEMS))
pager_open(arg_no_pager, false);
+
+ if (arg_action != ACTION_RUN)
skip_setup = true;
- }
if (arg_action == ACTION_HELP) {
retval = help();
@@ -2291,83 +2471,41 @@ int main(int argc, char *argv[]) {
retval = version();
goto finish;
} else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) {
- pager_open(arg_no_pager, false);
unit_dump_config_items(stdout);
retval = EXIT_SUCCESS;
goto finish;
}
- if (!arg_system &&
- !getenv("XDG_RUNTIME_DIR")) {
- log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set.");
- goto finish;
- }
-
assert_se(IN_SET(arg_action, ACTION_RUN, ACTION_TEST));
- /* Close logging fds, in order not to confuse fdset below */
- log_close();
-
- /* Remember open file descriptors for later deserialization */
- if (arg_action == ACTION_RUN) {
- r = fdset_new_fill(&fds);
- if (r < 0) {
- log_emergency_errno(r, "Failed to allocate fd set: %m");
- error_message = "Failed to allocate fd set";
- goto finish;
- } else
- fdset_cloexec(fds, true);
-
- if (arg_serialization)
- assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0);
-
- if (arg_system)
- /* Become a session leader if we aren't one yet. */
- setsid();
- }
-
/* Move out of the way, so that we won't block unmounts */
assert_se(chdir("/") == 0);
- /* Reset the console, but only if this is really init and we
- * are freshly booted */
- if (arg_system && arg_action == ACTION_RUN) {
-
- /* If we are init, we connect stdin/stdout/stderr to
- * /dev/null and make sure we don't have a controlling
- * tty. */
- release_terminal();
-
- if (getpid_cached() == 1 && !skip_setup)
- console_setup();
- }
-
- /* Open the logging devices, if possible and necessary */
- log_open();
-
- if (arg_show_status == _SHOW_STATUS_UNSET)
- arg_show_status = SHOW_STATUS_YES;
+ if (arg_action == ACTION_RUN) {
- /* Make sure we leave a core dump without panicing the
- * kernel. */
- if (getpid_cached() == 1) {
- install_crash_handler();
+ /* Close logging fds, in order not to confuse collecting passed fds and terminal logic below */
+ log_close();
- r = mount_cgroup_controllers(arg_join_controllers);
+ /* Remember open file descriptors for later deserialization */
+ r = collect_fds(&fds, &error_message);
if (r < 0)
goto finish;
+
+ /* Give up any control of the console, but make sure its initialized. */
+ setup_console_terminal(skip_setup);
+
+ /* Open the logging devices, if possible and necessary */
+ log_open();
}
log_execution_mode(&first_boot);
- if (arg_action == ACTION_RUN) {
- r = initialize_runtime(skip_setup,
- &saved_rlimit_nofile,
- &saved_rlimit_memlock,
- &error_message);
- if (r < 0)
- goto finish;
- }
+ r = initialize_runtime(skip_setup,
+ &saved_rlimit_nofile,
+ &saved_rlimit_memlock,
+ &error_message);
+ if (r < 0)
+ goto finish;
r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
arg_action == ACTION_TEST ? MANAGER_TEST_FULL : 0,
@@ -2416,36 +2554,20 @@ int main(int argc, char *argv[]) {
"Loaded units and determined initial transaction in %s.",
format_timespan(timespan, sizeof(timespan), after_startup - before_startup, 100 * USEC_PER_MSEC));
- if (arg_system) {
- _cleanup_free_ char *taint;
-
- taint = manager_taint_string(m);
- if (!isempty(taint))
- log_struct(LOG_NOTICE,
- LOG_MESSAGE("System is tainted: %s", taint),
- "TAINT=%s", taint,
- "MESSAGE_ID=" SD_MESSAGE_TAINTED_STR,
- NULL);
- }
-
if (arg_action == ACTION_TEST) {
- printf("-> By units:\n");
- manager_dump_units(m, stdout, "\t");
-
- printf("-> By jobs:\n");
- manager_dump_jobs(m, stdout, "\t");
+ test_summary(m);
retval = EXIT_SUCCESS;
goto finish;
}
- r = invoke_main_loop(m,
- &reexecute,
- &retval,
- &shutdown_verb,
- &fds,
- &switch_root_dir,
- &switch_root_init,
- &error_message);
+ (void) invoke_main_loop(m,
+ &reexecute,
+ &retval,
+ &shutdown_verb,
+ &fds,
+ &switch_root_dir,
+ &switch_root_init,
+ &error_message);
finish:
pager_close();
diff --git a/src/core/manager.c b/src/core/manager.c
index 81c4d5289b..e837a46f56 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -83,6 +83,7 @@
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
+#include "strxcpyx.h"
#include "terminal-util.h"
#include "time-util.h"
#include "transaction.h"
@@ -109,6 +110,7 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32
static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
+static int manager_dispatch_sigchld(sd_event_source *source, void *userdata);
static int manager_run_environment_generators(Manager *m);
static int manager_run_generators(Manager *m);
@@ -263,7 +265,7 @@ static int manager_dispatch_ask_password_fd(sd_event_source *source,
assert(m);
- flush_fd(fd);
+ (void) flush_fd(fd);
m->have_ask_password = have_ask_password();
if (m->have_ask_password < 0)
@@ -513,23 +515,31 @@ static int manager_setup_signals(Manager *m) {
return 0;
}
-static void manager_clean_environment(Manager *m) {
+static void manager_sanitize_environment(Manager *m) {
assert(m);
- /* Let's remove some environment variables that we
- * need ourselves to communicate with our clients */
+ /* Let's remove some environment variables that we need ourselves to communicate with our clients */
strv_env_unset_many(
m->environment,
- "NOTIFY_SOCKET",
+ "EXIT_CODE",
+ "EXIT_STATUS",
+ "INVOCATION_ID",
+ "JOURNAL_STREAM",
+ "LISTEN_FDNAMES",
+ "LISTEN_FDS",
+ "LISTEN_PID",
"MAINPID",
"MANAGERPID",
- "LISTEN_PID",
- "LISTEN_FDS",
- "LISTEN_FDNAMES",
+ "NOTIFY_SOCKET",
+ "REMOTE_ADDR",
+ "REMOTE_PORT",
+ "SERVICE_RESULT",
"WATCHDOG_PID",
"WATCHDOG_USEC",
- "INVOCATION_ID",
NULL);
+
+ /* Let's order the environment alphabetically, just to make it pretty */
+ strv_sort(m->environment);
}
static int manager_default_environment(Manager *m) {
@@ -556,8 +566,7 @@ static int manager_default_environment(Manager *m) {
if (!m->environment)
return -ENOMEM;
- manager_clean_environment(m);
- strv_sort(m->environment);
+ manager_sanitize_environment(m);
return 0;
}
@@ -627,6 +636,29 @@ static int manager_setup_run_queue(Manager *m) {
return 0;
}
+static int manager_setup_sigchld_event_source(Manager *m) {
+ int r;
+
+ assert(m);
+ assert(!m->sigchld_event_source);
+
+ r = sd_event_add_defer(m->event, &m->sigchld_event_source, manager_dispatch_sigchld, m);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(m->sigchld_event_source, SD_EVENT_PRIORITY_NORMAL-7);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_enabled(m->sigchld_event_source, SD_EVENT_OFF);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(m->sigchld_event_source, "manager-sigchld");
+
+ return 0;
+}
+
int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
Manager *m;
int r;
@@ -727,6 +759,10 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
if (r < 0)
goto fail;
+ r = manager_setup_sigchld_event_source(m);
+ if (r < 0)
+ goto fail;
+
m->udev = udev_new();
if (!m->udev) {
r = -ENOMEM;
@@ -810,7 +846,7 @@ static int manager_setup_notify(Manager *m) {
/* Process notification messages a bit earlier than SIGCHLD, so that we can still identify to which
* service an exit message belongs. */
- r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-7);
+ r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-8);
if (r < 0)
return log_error_errno(r, "Failed to set priority of notify event source: %m");
@@ -939,7 +975,7 @@ static int manager_setup_user_lookup_fd(Manager *m) {
/* Process even earlier than the notify event source, so that we always know first about valid UID/GID
* resolutions */
- r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-8);
+ r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-11);
if (r < 0)
return log_error_errno(errno, "Failed to set priority ot user lookup event source: %m");
@@ -1170,14 +1206,14 @@ Manager* manager_free(Manager *m) {
hashmap_free(m->units);
hashmap_free(m->units_by_invocation_id);
hashmap_free(m->jobs);
- hashmap_free(m->watch_pids1);
- hashmap_free(m->watch_pids2);
+ hashmap_free(m->watch_pids);
hashmap_free(m->watch_bus);
set_free(m->startup_units);
set_free(m->failed_units);
sd_event_source_unref(m->signal_event_source);
+ sd_event_source_unref(m->sigchld_event_source);
sd_event_source_unref(m->notify_event_source);
sd_event_source_unref(m->cgroups_agent_event_source);
sd_event_source_unref(m->time_change_event_source);
@@ -1872,27 +1908,40 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui
return 0;
}
-static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, FDSet *fds) {
- _cleanup_strv_free_ char **tags = NULL;
+static void manager_invoke_notify_message(
+ Manager *m,
+ Unit *u,
+ const struct ucred *ucred,
+ const char *buf,
+ FDSet *fds) {
assert(m);
assert(u);
+ assert(ucred);
assert(buf);
- tags = strv_split(buf, "\n\r");
- if (!tags) {
- log_oom();
+ if (u->notifygen == m->notifygen) /* Already invoked on this same unit in this same iteration? */
return;
- }
+ u->notifygen = m->notifygen;
+
+ if (UNIT_VTABLE(u)->notify_message) {
+ _cleanup_strv_free_ char **tags = NULL;
- if (UNIT_VTABLE(u)->notify_message)
- UNIT_VTABLE(u)->notify_message(u, pid, tags, fds);
- else if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
+ tags = strv_split(buf, NEWLINE);
+ if (!tags) {
+ log_oom();
+ return;
+ }
+
+ UNIT_VTABLE(u)->notify_message(u, ucred, tags, fds);
+
+ } else if (DEBUG_LOGGING) {
_cleanup_free_ char *x = NULL, *y = NULL;
- x = cescape(buf);
+ x = ellipsize(buf, 20, 90);
if (x)
- y = ellipsize(x, 20, 90);
+ y = cescape(x);
+
log_unit_debug(u, "Got notification message \"%s\", ignoring.", strnull(y));
}
}
@@ -1920,9 +1969,11 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
struct cmsghdr *cmsg;
struct ucred *ucred = NULL;
- Unit *u1, *u2, *u3;
+ _cleanup_free_ Unit **array_copy = NULL;
+ Unit *u1, *u2, **array;
int r, *fd_array = NULL;
unsigned n_fds = 0;
+ bool found = false;
ssize_t n;
assert(m);
@@ -1969,7 +2020,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
}
}
- if (!ucred || ucred->pid <= 0) {
+ if (!ucred || !pid_is_valid(ucred->pid)) {
log_warning("Received notify message without valid credentials. Ignoring.");
return 0;
}
@@ -1989,22 +2040,41 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
/* Make sure it's NUL-terminated. */
buf[n] = 0;
- /* Notify every unit that might be interested, but try
- * to avoid notifying the same one multiple times. */
+ /* Increase the generation counter used for filtering out duplicate unit invocations. */
+ m->notifygen++;
+
+ /* Notify every unit that might be interested, which might be multiple. */
u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid);
- if (u1)
- manager_invoke_notify_message(m, u1, ucred->pid, buf, fds);
+ u2 = hashmap_get(m->watch_pids, PID_TO_PTR(ucred->pid));
+ array = hashmap_get(m->watch_pids, PID_TO_PTR(-ucred->pid));
+ if (array) {
+ size_t k = 0;
- u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid));
- if (u2 && u2 != u1)
- manager_invoke_notify_message(m, u2, ucred->pid, buf, fds);
+ while (array[k])
+ k++;
- u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid));
- if (u3 && u3 != u2 && u3 != u1)
- manager_invoke_notify_message(m, u3, ucred->pid, buf, fds);
+ array_copy = newdup(Unit*, array, k+1);
+ if (!array_copy)
+ log_oom();
+ }
+ /* And now invoke the per-unit callbacks. Note that manager_invoke_notify_message() will handle duplicate units
+ * make sure we only invoke each unit's handler once. */
+ if (u1) {
+ manager_invoke_notify_message(m, u1, ucred, buf, fds);
+ found = true;
+ }
+ if (u2) {
+ manager_invoke_notify_message(m, u2, ucred, buf, fds);
+ found = true;
+ }
+ if (array_copy)
+ for (size_t i = 0; array_copy[i]; i++) {
+ manager_invoke_notify_message(m, array_copy[i], ucred, buf, fds);
+ found = true;
+ }
- if (!u1 && !u2 && !u3)
- log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
+ if (!found)
+ log_warning("Cannot find unit for notify message of PID "PID_FMT", ignoring.", ucred->pid);
if (fdset_size(fds) > 0)
log_warning("Got extra auxiliary fds with notification message, closing them.");
@@ -2012,89 +2082,111 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
return 0;
}
-static void invoke_sigchld_event(Manager *m, Unit *u, const siginfo_t *si) {
- uint64_t iteration;
+static void manager_invoke_sigchld_event(
+ Manager *m,
+ Unit *u,
+ const siginfo_t *si) {
assert(m);
assert(u);
assert(si);
- sd_event_get_iteration(m->event, &iteration);
-
- log_unit_debug(u, "Child "PID_FMT" belongs to %s", si->si_pid, u->id);
+ /* Already invoked the handler of this unit in this iteration? Then don't process this again */
+ if (u->sigchldgen == m->sigchldgen)
+ return;
+ u->sigchldgen = m->sigchldgen;
+ log_unit_debug(u, "Child "PID_FMT" belongs to %s.", si->si_pid, u->id);
unit_unwatch_pid(u, si->si_pid);
- if (UNIT_VTABLE(u)->sigchld_event) {
- if (set_size(u->pids) <= 1 ||
- iteration != u->sigchldgen ||
- unit_main_pid(u) == si->si_pid ||
- unit_control_pid(u) == si->si_pid) {
- UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status);
- u->sigchldgen = iteration;
- } else
- log_debug("%s already issued a sigchld this iteration %" PRIu64 ", skipping. Pids still being watched %d", u->id, iteration, set_size(u->pids));
- }
+ if (UNIT_VTABLE(u)->sigchld_event)
+ UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status);
}
-static int manager_dispatch_sigchld(Manager *m) {
+static int manager_dispatch_sigchld(sd_event_source *source, void *userdata) {
+ Manager *m = userdata;
+ siginfo_t si = {};
+ int r;
+
+ assert(source);
assert(m);
- for (;;) {
- siginfo_t si = {};
+ /* First we call waitd() for a PID and do not reap the zombie. That way we can still access /proc/$PID for it
+ * while it is a zombie. */
- /* First we call waitd() for a PID and do not reap the
- * zombie. That way we can still access /proc/$PID for
- * it while it is a zombie. */
- if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) {
+ if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) {
- if (errno == ECHILD)
- break;
+ if (errno == ECHILD)
+ goto turn_off;
- if (errno == EINTR)
- continue;
+ log_error_errno(errno, "Failed to peek for child with waitid(), ignoring: %m");
+ return 0;
+ }
- return -errno;
+ if (si.si_pid <= 0)
+ goto turn_off;
+
+ if (IN_SET(si.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED)) {
+ _cleanup_free_ Unit **array_copy = NULL;
+ _cleanup_free_ char *name = NULL;
+ Unit *u1, *u2, **array;
+
+ (void) get_process_comm(si.si_pid, &name);
+
+ log_debug("Child "PID_FMT" (%s) died (code=%s, status=%i/%s)",
+ si.si_pid, strna(name),
+ sigchld_code_to_string(si.si_code),
+ si.si_status,
+ strna(si.si_code == CLD_EXITED
+ ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL)
+ : signal_to_string(si.si_status)));
+
+ /* Increase the generation counter used for filtering out duplicate unit invocations */
+ m->sigchldgen++;
+
+ /* And now figure out the unit this belongs to, it might be multiple... */
+ u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid);
+ u2 = hashmap_get(m->watch_pids, PID_TO_PTR(si.si_pid));
+ array = hashmap_get(m->watch_pids, PID_TO_PTR(-si.si_pid));
+ if (array) {
+ size_t n = 0;
+
+ /* Cound how many entries the array has */
+ while (array[n])
+ n++;
+
+ /* Make a copy of the array so that we don't trip up on the array changing beneath us */
+ array_copy = newdup(Unit*, array, n+1);
+ if (!array_copy)
+ log_oom();
}
- if (si.si_pid <= 0)
- break;
+ /* Finally, execute them all. Note that u1, u2 and the array might contain duplicates, but
+ * that's fine, manager_invoke_sigchld_event() will ensure we only invoke the handlers once for
+ * each iteration. */
+ if (u1)
+ manager_invoke_sigchld_event(m, u1, &si);
+ if (u2)
+ manager_invoke_sigchld_event(m, u2, &si);
+ if (array_copy)
+ for (size_t i = 0; array_copy[i]; i++)
+ manager_invoke_sigchld_event(m, array_copy[i], &si);
+ }
- if (IN_SET(si.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED)) {
- _cleanup_free_ char *name = NULL;
- Unit *u1, *u2, *u3;
-
- get_process_comm(si.si_pid, &name);
-
- log_debug("Child "PID_FMT" (%s) died (code=%s, status=%i/%s)",
- si.si_pid, strna(name),
- sigchld_code_to_string(si.si_code),
- si.si_status,
- strna(si.si_code == CLD_EXITED
- ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL)
- : signal_to_string(si.si_status)));
-
- /* And now figure out the unit this belongs
- * to, it might be multiple... */
- u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid);
- if (u1)
- invoke_sigchld_event(m, u1, &si);
- u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(si.si_pid));
- if (u2 && u2 != u1)
- invoke_sigchld_event(m, u2, &si);
- u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(si.si_pid));
- if (u3 && u3 != u2 && u3 != u1)
- invoke_sigchld_event(m, u3, &si);
- }
+ /* And now, we actually reap the zombie. */
+ if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
+ log_error_errno(errno, "Failed to dequeue child, ignoring: %m");
+ return 0;
+ }
- /* And now, we actually reap the zombie. */
- if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
- if (errno == EINTR)
- continue;
+ return 0;
- return -errno;
- }
- }
+turn_off:
+ /* All children processed for now, turn off event source */
+
+ r = sd_event_source_set_enabled(m->sigchld_event_source, SD_EVENT_OFF);
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable SIGCHLD event source: %m");
return 0;
}
@@ -2126,7 +2218,6 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
Manager *m = userdata;
ssize_t n;
struct signalfd_siginfo sfsi;
- bool sigchld = false;
int r;
assert(m);
@@ -2137,195 +2228,192 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
return 0;
}
- for (;;) {
- n = read(m->signal_fd, &sfsi, sizeof(sfsi));
- if (n != sizeof(sfsi)) {
- if (n >= 0) {
- log_warning("Truncated read from signal fd (%zu bytes)!", n);
- return 0;
- }
+ n = read(m->signal_fd, &sfsi, sizeof(sfsi));
+ if (n != sizeof(sfsi)) {
+ if (n >= 0) {
+ log_warning("Truncated read from signal fd (%zu bytes), ignoring!", n);
+ return 0;
+ }
- if (IN_SET(errno, EINTR, EAGAIN))
- break;
+ if (IN_SET(errno, EINTR, EAGAIN))
+ return 0;
- /* We return an error here, which will kill this handler,
- * to avoid a busy loop on read error. */
- return log_error_errno(errno, "Reading from signal fd failed: %m");
- }
+ /* We return an error here, which will kill this handler,
+ * to avoid a busy loop on read error. */
+ return log_error_errno(errno, "Reading from signal fd failed: %m");
+ }
- log_received_signal(sfsi.ssi_signo == SIGCHLD ||
- (sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m))
- ? LOG_DEBUG : LOG_INFO,
- &sfsi);
+ log_received_signal(sfsi.ssi_signo == SIGCHLD ||
+ (sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m))
+ ? LOG_DEBUG : LOG_INFO,
+ &sfsi);
- switch (sfsi.ssi_signo) {
+ switch (sfsi.ssi_signo) {
- case SIGCHLD:
- sigchld = true;
- break;
+ case SIGCHLD:
+ r = sd_event_source_set_enabled(m->sigchld_event_source, SD_EVENT_ON);
+ if (r < 0)
+ log_warning_errno(r, "Failed to enable SIGCHLD even source, ignoring: %m");
- case SIGTERM:
- if (MANAGER_IS_SYSTEM(m)) {
- /* This is for compatibility with the
- * original sysvinit */
- r = verify_run_space_and_log("Refusing to reexecute");
- if (r >= 0)
- m->exit_code = MANAGER_REEXECUTE;
- break;
- }
+ break;
- _fallthrough_;
- case SIGINT:
- if (MANAGER_IS_SYSTEM(m))
- manager_handle_ctrl_alt_del(m);
- else
- manager_start_target(m, SPECIAL_EXIT_TARGET,
- JOB_REPLACE_IRREVERSIBLY);
+ case SIGTERM:
+ if (MANAGER_IS_SYSTEM(m)) {
+ /* This is for compatibility with the
+ * original sysvinit */
+ r = verify_run_space_and_log("Refusing to reexecute");
+ if (r >= 0)
+ m->exit_code = MANAGER_REEXECUTE;
break;
+ }
- case SIGWINCH:
- if (MANAGER_IS_SYSTEM(m))
- manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
+ _fallthrough_;
+ case SIGINT:
+ if (MANAGER_IS_SYSTEM(m))
+ manager_handle_ctrl_alt_del(m);
+ else
+ manager_start_target(m, SPECIAL_EXIT_TARGET,
+ JOB_REPLACE_IRREVERSIBLY);
+ break;
- /* This is a nop on non-init */
- break;
+ case SIGWINCH:
+ if (MANAGER_IS_SYSTEM(m))
+ manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
- case SIGPWR:
- if (MANAGER_IS_SYSTEM(m))
- manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
+ /* This is a nop on non-init */
+ break;
- /* This is a nop on non-init */
- break;
+ case SIGPWR:
+ if (MANAGER_IS_SYSTEM(m))
+ manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
- case SIGUSR1: {
- Unit *u;
+ /* This is a nop on non-init */
+ break;
- u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
+ case SIGUSR1: {
+ Unit *u;
- if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
- log_info("Trying to reconnect to bus...");
- bus_init(m, true);
- }
+ u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
- if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) {
- log_info("Loading D-Bus service...");
- manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE);
- }
+ if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
+ log_info("Trying to reconnect to bus...");
+ bus_init(m, true);
+ }
+
+ if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) {
+ log_info("Loading D-Bus service...");
+ manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE);
+ }
+ break;
+ }
+
+ case SIGUSR2: {
+ _cleanup_free_ char *dump = NULL;
+
+ r = manager_get_dump_string(m, &dump);
+ if (r < 0) {
+ log_warning_errno(errno, "Failed to acquire manager dump: %m");
break;
}
- case SIGUSR2: {
- _cleanup_free_ char *dump = NULL;
+ log_dump(LOG_INFO, dump);
+ break;
+ }
- r = manager_get_dump_string(m, &dump);
- if (r < 0) {
- log_warning_errno(errno, "Failed to acquire manager dump: %m");
- break;
- }
+ case SIGHUP:
+ r = verify_run_space_and_log("Refusing to reload");
+ if (r >= 0)
+ m->exit_code = MANAGER_RELOAD;
+ break;
+
+ default: {
+
+ /* Starting SIGRTMIN+0 */
+ static const struct {
+ const char *target;
+ JobMode mode;
+ } target_table[] = {
+ [0] = { SPECIAL_DEFAULT_TARGET, JOB_ISOLATE },
+ [1] = { SPECIAL_RESCUE_TARGET, JOB_ISOLATE },
+ [2] = { SPECIAL_EMERGENCY_TARGET, JOB_ISOLATE },
+ [3] = { SPECIAL_HALT_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ [4] = { SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ [5] = { SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ [6] = { SPECIAL_KEXEC_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ };
+
+ /* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */
+ static const ManagerExitCode code_table[] = {
+ [0] = MANAGER_HALT,
+ [1] = MANAGER_POWEROFF,
+ [2] = MANAGER_REBOOT,
+ [3] = MANAGER_KEXEC,
+ };
- log_dump(LOG_INFO, dump);
+ if ((int) sfsi.ssi_signo >= SIGRTMIN+0 &&
+ (int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) {
+ int idx = (int) sfsi.ssi_signo - SIGRTMIN;
+ manager_start_target(m, target_table[idx].target,
+ target_table[idx].mode);
break;
}
- case SIGHUP:
- r = verify_run_space_and_log("Refusing to reload");
- if (r >= 0)
- m->exit_code = MANAGER_RELOAD;
+ if ((int) sfsi.ssi_signo >= SIGRTMIN+13 &&
+ (int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(code_table)) {
+ m->exit_code = code_table[sfsi.ssi_signo - SIGRTMIN - 13];
break;
+ }
- default: {
-
- /* Starting SIGRTMIN+0 */
- static const struct {
- const char *target;
- JobMode mode;
- } target_table[] = {
- [0] = { SPECIAL_DEFAULT_TARGET, JOB_ISOLATE },
- [1] = { SPECIAL_RESCUE_TARGET, JOB_ISOLATE },
- [2] = { SPECIAL_EMERGENCY_TARGET, JOB_ISOLATE },
- [3] = { SPECIAL_HALT_TARGET, JOB_REPLACE_IRREVERSIBLY },
- [4] = { SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY },
- [5] = { SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY },
- [6] = { SPECIAL_KEXEC_TARGET, JOB_REPLACE_IRREVERSIBLY }
- };
-
- /* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */
- static const ManagerExitCode code_table[] = {
- [0] = MANAGER_HALT,
- [1] = MANAGER_POWEROFF,
- [2] = MANAGER_REBOOT,
- [3] = MANAGER_KEXEC
- };
-
- if ((int) sfsi.ssi_signo >= SIGRTMIN+0 &&
- (int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) {
- int idx = (int) sfsi.ssi_signo - SIGRTMIN;
- manager_start_target(m, target_table[idx].target,
- target_table[idx].mode);
- break;
- }
+ switch (sfsi.ssi_signo - SIGRTMIN) {
- if ((int) sfsi.ssi_signo >= SIGRTMIN+13 &&
- (int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(code_table)) {
- m->exit_code = code_table[sfsi.ssi_signo - SIGRTMIN - 13];
- break;
- }
+ case 20:
+ manager_set_show_status(m, SHOW_STATUS_YES);
+ break;
+
+ case 21:
+ manager_set_show_status(m, SHOW_STATUS_NO);
+ break;
+
+ case 22:
+ log_set_max_level(LOG_DEBUG);
+ log_info("Setting log level to debug.");
+ break;
+
+ case 23:
+ log_set_max_level(LOG_INFO);
+ log_info("Setting log level to info.");
+ break;
- switch (sfsi.ssi_signo - SIGRTMIN) {
-
- case 20:
- manager_set_show_status(m, SHOW_STATUS_YES);
- break;
-
- case 21:
- manager_set_show_status(m, SHOW_STATUS_NO);
- break;
-
- case 22:
- log_set_max_level(LOG_DEBUG);
- log_info("Setting log level to debug.");
- break;
-
- case 23:
- log_set_max_level(LOG_INFO);
- log_info("Setting log level to info.");
- break;
-
- case 24:
- if (MANAGER_IS_USER(m)) {
- m->exit_code = MANAGER_EXIT;
- return 0;
- }
-
- /* This is a nop on init */
- break;
-
- case 26:
- case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */
- log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
- log_notice("Setting log target to journal-or-kmsg.");
- break;
-
- case 27:
- log_set_target(LOG_TARGET_CONSOLE);
- log_notice("Setting log target to console.");
- break;
-
- case 28:
- log_set_target(LOG_TARGET_KMSG);
- log_notice("Setting log target to kmsg.");
- break;
-
- default:
- log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo));
+ case 24:
+ if (MANAGER_IS_USER(m)) {
+ m->exit_code = MANAGER_EXIT;
+ return 0;
}
- }
- }
- }
- if (sigchld)
- manager_dispatch_sigchld(m);
+ /* This is a nop on init */
+ break;
+
+ case 26:
+ case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */
+ log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
+ log_notice("Setting log target to journal-or-kmsg.");
+ break;
+
+ case 27:
+ log_set_target(LOG_TARGET_CONSOLE);
+ log_notice("Setting log target to console.");
+ break;
+
+ case 28:
+ log_set_target(LOG_TARGET_KMSG);
+ log_notice("Setting log target to kmsg.");
+ break;
+
+ default:
+ log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo));
+ }
+ }}
return 0;
}
@@ -2362,8 +2450,15 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32
assert(m);
assert(m->idle_pipe[2] == fd);
+ /* There's at least one Type=idle child that just gave up on us waiting for the boot process to complete. Let's
+ * now turn off any further console output if there's at least one service that needs console access, so that
+ * from now on our own output should not spill into that service's output anymore. After all, we support
+ * Type=idle only to beautify console output and it generally is set on services that want to own the console
+ * exclusively without our interference. */
m->no_console_output = m->n_on_console > 0;
+ /* Acknowledge the child's request, and let all all other children know too that they shouldn't wait any longer
+ * by closing the pipes towards them, which is what they are waiting for. */
manager_close_idle_pipe(m);
return 0;
@@ -2400,11 +2495,10 @@ int manager_loop(Manager *m) {
manager_check_finished(m);
- /* There might still be some zombies hanging around from
- * before we were exec()'ed. Let's reap them. */
- r = manager_dispatch_sigchld(m);
+ /* There might still be some zombies hanging around from before we were exec()'ed. Let's reap them. */
+ r = sd_event_source_set_enabled(m->sigchld_event_source, SD_EVENT_ON);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to enable SIGCHLD event source: %m");
while (m->exit_code == MANAGER_OK) {
usec_t wait_usec;
@@ -2643,6 +2737,8 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
fprintf(f, "n-failed-jobs=%u\n", m->n_failed_jobs);
fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr));
fprintf(f, "ready-sent=%s\n", yes_no(m->ready_sent));
+ fprintf(f, "taint-logged=%s\n", yes_no(m->taint_logged));
+ fprintf(f, "service-watchdogs=%s\n", yes_no(m->service_watchdogs));
for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
/* The userspace and finish timestamps only apply to the host system, hence only serialize them there */
@@ -2805,6 +2901,24 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
else
m->ready_sent = m->ready_sent || b;
+ } else if ((val = startswith(l, "taint-logged="))) {
+ int b;
+
+ b = parse_boolean(val);
+ if (b < 0)
+ log_notice("Failed to parse taint-logged flag %s", val);
+ else
+ m->taint_logged = m->taint_logged || b;
+
+ } else if ((val = startswith(l, "service-watchdogs="))) {
+ int b;
+
+ b = parse_boolean(val);
+ if (b < 0)
+ log_notice("Failed to parse service-watchdogs flag %s", val);
+ else
+ m->service_watchdogs = b;
+
} else if (startswith(l, "env=")) {
r = deserialize_environment(&m->environment, l);
if (r == -ENOMEM)
@@ -3026,6 +3140,9 @@ int manager_reload(Manager *m) {
manager_vacuum_uid_refs(m);
manager_vacuum_gid_refs(m);
+ /* It might be safe to log to the journal now. */
+ manager_recheck_journal(m);
+
/* Sync current state of bus names with our set of listening units */
if (m->api_bus)
manager_sync_bus_names(m, m->api_bus);
@@ -3062,6 +3179,27 @@ bool manager_unit_inactive_or_pending(Manager *m, const char *name) {
return unit_inactive_or_pending(u);
}
+static void log_taint_string(Manager *m) {
+ _cleanup_free_ char *taint = NULL;
+
+ assert(m);
+
+ if (MANAGER_IS_USER(m) || m->taint_logged)
+ return;
+
+ m->taint_logged = true; /* only check for taint once */
+
+ taint = manager_taint_string(m);
+ if (isempty(taint))
+ return;
+
+ log_struct(LOG_NOTICE,
+ LOG_MESSAGE("System is tainted: %s", taint),
+ "TAINT=%s", taint,
+ "MESSAGE_ID=" SD_MESSAGE_TAINTED_STR,
+ NULL);
+}
+
static void manager_notify_finished(Manager *m) {
char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX];
usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec;
@@ -3070,6 +3208,11 @@ static void manager_notify_finished(Manager *m) {
return;
if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ char buf[FORMAT_TIMESPAN_MAX + STRLEN(" (firmware) + ") + FORMAT_TIMESPAN_MAX + STRLEN(" (loader) + ")]
+ = {};
+ char *p = buf;
+ size_t size = sizeof buf;
/* Note that MANAGER_TIMESTAMP_KERNEL's monotonic value is always at 0, and
* MANAGER_TIMESTAMP_FIRMWARE's and MANAGER_TIMESTAMP_LOADER's monotonic value should be considered
@@ -3080,6 +3223,11 @@ static void manager_notify_finished(Manager *m) {
userspace_usec = m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic - m->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic;
total_usec = m->timestamps[MANAGER_TIMESTAMP_FIRMWARE].monotonic + m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic;
+ if (firmware_usec > 0)
+ size = strpcpyf(&p, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), firmware_usec, USEC_PER_MSEC));
+ if (loader_usec > 0)
+ size = strpcpyf(&p, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), loader_usec, USEC_PER_MSEC));
+
if (dual_timestamp_is_set(&m->timestamps[MANAGER_TIMESTAMP_INITRD])) {
/* The initrd case on bare-metal*/
@@ -3091,7 +3239,8 @@ static void manager_notify_finished(Manager *m) {
"KERNEL_USEC="USEC_FMT, kernel_usec,
"INITRD_USEC="USEC_FMT, initrd_usec,
"USERSPACE_USEC="USEC_FMT, userspace_usec,
- LOG_MESSAGE("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
+ LOG_MESSAGE("Startup finished in %s%s (kernel) + %s (initrd) + %s (userspace) = %s.",
+ buf,
format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC),
format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
@@ -3107,14 +3256,15 @@ static void manager_notify_finished(Manager *m) {
"MESSAGE_ID=" SD_MESSAGE_STARTUP_FINISHED_STR,
"KERNEL_USEC="USEC_FMT, kernel_usec,
"USERSPACE_USEC="USEC_FMT, userspace_usec,
- LOG_MESSAGE("Startup finished in %s (kernel) + %s (userspace) = %s.",
+ LOG_MESSAGE("Startup finished in %s%s (kernel) + %s (userspace) = %s.",
+ buf,
format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)),
NULL);
}
} else {
- /* The container case */
+ /* The container and --user case */
firmware_usec = loader_usec = initrd_usec = kernel_usec = 0;
total_usec = userspace_usec = m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic - m->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic;
@@ -3134,32 +3284,55 @@ static void manager_notify_finished(Manager *m) {
"STATUS=Startup finished in %s.",
format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC));
m->ready_sent = true;
+
+ log_taint_string(m);
}
-void manager_check_finished(Manager *m) {
+static void manager_send_ready(Manager *m) {
assert(m);
- if (MANAGER_IS_RELOADING(m))
+ /* We send READY=1 on reaching basic.target only when running in --user mode. */
+ if (!MANAGER_IS_USER(m) || m->ready_sent)
+ return;
+
+ m->ready_sent = true;
+
+ sd_notifyf(false,
+ "READY=1\n"
+ "STATUS=Reached " SPECIAL_BASIC_TARGET ".");
+}
+
+static void manager_check_basic_target(Manager *m) {
+ Unit *u;
+
+ assert(m);
+
+ /* Small shortcut */
+ if (m->ready_sent && m->taint_logged)
return;
- /* Verify that we are actually running currently. Initially
- * the exit code is set to invalid, and during operation it is
- * then set to MANAGER_OK */
- if (m->exit_code != MANAGER_OK)
+ u = manager_get_unit(m, SPECIAL_BASIC_TARGET);
+ if (!u || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
return;
/* For user managers, send out READY=1 as soon as we reach basic.target */
- if (MANAGER_IS_USER(m) && !m->ready_sent) {
- Unit *u;
+ manager_send_ready(m);
- u = manager_get_unit(m, SPECIAL_BASIC_TARGET);
- if (u && !u->job) {
- sd_notifyf(false,
- "READY=1\n"
- "STATUS=Reached " SPECIAL_BASIC_TARGET ".");
- m->ready_sent = true;
- }
- }
+ /* Log the taint string as soon as we reach basic.target */
+ log_taint_string(m);
+}
+
+void manager_check_finished(Manager *m) {
+ assert(m);
+
+ if (MANAGER_IS_RELOADING(m))
+ return;
+
+ /* Verify that we have entered the event loop already, and not left it again. */
+ if (!MANAGER_IS_RUNNING(m))
+ return;
+
+ manager_check_basic_target(m);
if (hashmap_size(m->jobs) > 0) {
if (m->jobs_in_progress_event_source)
@@ -3308,8 +3481,7 @@ int manager_environment_add(Manager *m, char **minus, char **plus) {
strv_free(b);
m->environment = l;
- manager_clean_environment(m);
- strv_sort(m->environment);
+ manager_sanitize_environment(m);
return 0;
}
@@ -3333,28 +3505,50 @@ int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) {
return 0;
}
-void manager_recheck_journal(Manager *m) {
+static bool manager_journal_is_running(Manager *m) {
Unit *u;
assert(m);
+ /* If we are the user manager we can safely assume that the journal is up */
if (!MANAGER_IS_SYSTEM(m))
- return;
+ return true;
+ /* Check that the socket is not only up, but in RUNNING state */
u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET);
- if (u && SOCKET(u)->state != SOCKET_RUNNING) {
- log_close_journal();
- return;
- }
+ if (!u)
+ return false;
+ if (SOCKET(u)->state != SOCKET_RUNNING)
+ return false;
+ /* Similar, check if the daemon itself is fully up, too */
u = manager_get_unit(m, SPECIAL_JOURNALD_SERVICE);
- if (u && SERVICE(u)->state != SERVICE_RUNNING) {
- log_close_journal();
+ if (!u)
+ return false;
+ if (SERVICE(u)->state != SERVICE_RUNNING)
+ return false;
+
+ return true;
+}
+
+void manager_recheck_journal(Manager *m) {
+
+ assert(m);
+
+ /* Don't bother with this unless we are in the special situation of being PID 1 */
+ if (getpid_cached() != 1)
return;
- }
- /* Hmm, OK, so the socket is fully up and the service is up
- * too, then let's make use of the thing. */
+ if (manager_journal_is_running(m)) {
+
+ /* The journal is fully and entirely up? If so, let's permit logging to it, if that's configured. */
+ log_set_prohibit_ipc(false);
+ } else {
+
+ /* If the journal is down, don't ever log to it, otherwise we might end up deadlocking ourselves as we
+ * might trigger an activation ourselves we can't fulfill */
+ log_set_prohibit_ipc(true);
+ }
log_open();
}
@@ -3392,10 +3586,7 @@ static bool manager_get_show_status(Manager *m, StatusType type) {
if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0)
return false;
- if (m->show_status > 0)
- return true;
-
- return false;
+ return m->show_status > 0;
}
const char *manager_get_confirm_spawn(Manager *m) {
@@ -3919,6 +4110,21 @@ char *manager_taint_string(Manager *m) {
return buf;
}
+void manager_ref_console(Manager *m) {
+ assert(m);
+
+ m->n_on_console++;
+}
+
+void manager_unref_console(Manager *m) {
+
+ assert(m->n_on_console > 0);
+ m->n_on_console--;
+
+ if (m->n_on_console == 0)
+ m->no_console_output = false; /* unset no_console_output flag, since the console is definitely free now */
+}
+
static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
[MANAGER_INITIALIZING] = "initializing",
[MANAGER_STARTING] = "starting",
diff --git a/src/core/manager.h b/src/core/manager.h
index 902af2609d..0eed67b46a 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -145,14 +145,14 @@ struct Manager {
sd_event *event;
- /* We use two hash tables here, since the same PID might be
- * watched by two different units: once the unit that forked
- * it off, and possibly a different unit to which it was
- * joined as cgroup member. Since we know that it is either
- * one or two units for each PID we just use to hashmaps
- * here. */
- Hashmap *watch_pids1; /* pid => Unit object n:1 */
- Hashmap *watch_pids2; /* pid => Unit object n:1 */
+ /* This maps PIDs we care about to units that are interested in. We allow multiple units to he interested in
+ * the same PID and multiple PIDs to be relevant to the same unit. Since in most cases only a single unit will
+ * be interested in the same PID we use a somewhat special encoding here: the first unit interested in a PID is
+ * stored directly in the hashmap, keyed by the PID unmodified. If there are other units interested too they'll
+ * be stored in a NULL-terminated array, and keyed by the negative PID. This is safe as pid_t is signed and
+ * negative PIDs are not used for regular processes but process groups, which we don't care about in this
+ * context, but this allows us to use the negative range for our own purposes. */
+ Hashmap *watch_pids; /* pid => unit as well as -pid => array of units */
/* A set contains all units which cgroup should be refreshed after startup */
Set *startup_units;
@@ -172,6 +172,8 @@ struct Manager {
int signal_fd;
sd_event_source *signal_event_source;
+ sd_event_source *sigchld_event_source;
+
int time_change_fd;
sd_event_source *time_change_event_source;
@@ -261,8 +263,15 @@ struct Manager {
bool taint_usr:1;
+ /* Have we already sent out the READY=1 notification? */
bool ready_sent:1;
+ /* Have we already printed the taint line if necessary? */
+ bool taint_logged:1;
+
+ /* Have we ever changed the "kernel.pid_max" sysctl? */
+ bool sysctl_pid_max_changed:1;
+
unsigned test_run_flags:8;
/* If non-zero, exit with the following value when the systemd
@@ -273,6 +282,7 @@ struct Manager {
ShowStatus show_status;
char *confirm_spawn;
bool no_console_output;
+ bool service_watchdogs;
ExecOutput default_std_output, default_std_error;
@@ -343,8 +353,13 @@ struct Manager {
int first_boot; /* tri-state */
- /* prefixes of e.g. RuntimeDirectory= */
+ /* Prefixes of e.g. RuntimeDirectory= */
char *prefix[_EXEC_DIRECTORY_TYPE_MAX];
+
+ /* Used in the SIGCHLD and sd_notify() message invocation logic to avoid that we dispatch the same event
+ * multiple times on the same unit. */
+ unsigned sigchldgen;
+ unsigned notifygen;
};
#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM)
@@ -354,6 +369,9 @@ struct Manager {
#define MANAGER_IS_FINISHED(m) (dual_timestamp_is_set((m)->timestamps + MANAGER_TIMESTAMP_FINISH))
+/* The exit code is set to OK as soon as we enter the main loop, and set otherwise as soon as we are done with it */
+#define MANAGER_IS_RUNNING(m) ((m)->exit_code == MANAGER_OK)
+
int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **m);
Manager* manager_free(Manager *m);
@@ -437,6 +455,9 @@ void manager_deserialize_gid_refs_one(Manager *m, const char *value);
char *manager_taint_string(Manager *m);
+void manager_ref_console(Manager *m);
+void manager_unref_console(Manager *m);
+
const char *manager_state_to_string(ManagerState m) _const_;
ManagerState manager_state_from_string(const char *s) _pure_;
diff --git a/src/core/meson.build b/src/core/meson.build
index 535ccde468..bc034082a5 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -60,6 +60,8 @@ libcore_la_sources = '''
dbus-timer.h
dbus-unit.c
dbus-unit.h
+ dbus-util.c
+ dbus-util.h
dbus.c
dbus.h
device.c
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 7171d8fda4..a0c5f5aaae 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -155,10 +155,12 @@ bool mount_point_ignore(const char *path) {
}
static int mount_one(const MountPoint *p, bool relabel) {
- int r;
+ int r, priority;
assert(p);
+ priority = (p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG;
+
if (p->condition_fn && !p->condition_fn())
return 0;
@@ -168,7 +170,7 @@ static int mount_one(const MountPoint *p, bool relabel) {
r = path_is_mount_point(p->where, NULL, AT_SYMLINK_FOLLOW);
if (r < 0 && r != -ENOENT) {
- log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where);
+ log_full_errno(priority, r, "Failed to determine whether %s is a mount point: %m", p->where);
return (p->mode & MNT_FATAL) ? r : 0;
}
if (r > 0)
@@ -196,7 +198,7 @@ static int mount_one(const MountPoint *p, bool relabel) {
p->type,
p->flags,
p->options) < 0) {
- log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, errno, "Failed to mount %s at %s: %m", p->type, p->where);
+ log_full_errno(priority, errno, "Failed to mount %s at %s: %m", p->type, p->where);
return (p->mode & MNT_FATAL) ? -errno : 0;
}
@@ -205,10 +207,13 @@ static int mount_one(const MountPoint *p, bool relabel) {
(void) label_fix(p->where, false, false);
if (p->mode & MNT_CHECK_WRITABLE) {
- r = access(p->where, W_OK);
- if (r < 0) {
+ if (access(p->where, W_OK) < 0) {
+ r = -errno;
+
(void) umount(p->where);
(void) rmdir(p->where);
+
+ log_full_errno(priority, r, "Mount point %s not writable after mounting: %m", p->where);
return (p->mode & MNT_FATAL) ? r : 0;
}
}
diff --git a/src/core/mount.c b/src/core/mount.c
index b25bb9cb40..4c12542bd7 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -55,7 +55,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter);
static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = {
[MOUNT_DEAD] = UNIT_INACTIVE,
[MOUNT_MOUNTING] = UNIT_ACTIVATING,
- [MOUNT_MOUNTING_DONE] = UNIT_ACTIVE,
+ [MOUNT_MOUNTING_DONE] = UNIT_ACTIVATING,
[MOUNT_MOUNTED] = UNIT_ACTIVE,
[MOUNT_REMOUNTING] = UNIT_RELOADING,
[MOUNT_UNMOUNTING] = UNIT_DEACTIVATING,
@@ -164,6 +164,10 @@ static void mount_init(Unit *u) {
assert(u->load_state == UNIT_STUB);
m->timeout_usec = u->manager->default_timeout_start_usec;
+
+ m->exec_context.std_output = u->manager->default_std_output;
+ m->exec_context.std_error = u->manager->default_std_error;
+
m->directory_mode = 0755;
/* We need to make sure that /usr/bin/mount is always called
@@ -938,7 +942,7 @@ static void mount_enter_mounting(Mount *m) {
assert(m);
- r = unit_fail_if_symlink(UNIT(m), m->where);
+ r = unit_fail_if_noncanonical(UNIT(m), m->where);
if (r < 0)
goto fail;
@@ -1131,10 +1135,6 @@ static int mount_reload(Unit *u) {
Mount *m = MOUNT(u);
assert(m);
-
- if (m->state == MOUNT_MOUNTING_DONE) /* not yet ready to reload, try again */
- return -EAGAIN;
-
assert(m->state == MOUNT_MOUNTED);
mount_enter_remounting(m);
@@ -1276,23 +1276,25 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
log_unit_full(u, f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
"Mount process exited, code=%s status=%i", sigchld_code_to_string(code), status);
- /* Note that mount(8) returning and the kernel sending us a mount table change event might happen
- * out-of-order. If an operation succeed we assume the kernel will follow soon too and already change into the
- * resulting state. If it fails we check if the kernel still knows about the mount. and change state
- * accordingly. */
+ /* Note that due to the io event priority logic, we can be sure the new mountinfo is loaded
+ * before we process the SIGCHLD for the mount command. */
switch (m->state) {
case MOUNT_MOUNTING:
- case MOUNT_MOUNTING_DONE:
+ /* Our mount point has not appeared in mountinfo. Something went wrong. */
- if (f == MOUNT_SUCCESS || m->from_proc_self_mountinfo)
- /* If /bin/mount returned success, or if we see the mount point in /proc/self/mountinfo we are
- * happy. If we see the first condition first, we should see the second condition
- * immediately after – or /bin/mount lies to us and is broken. */
- mount_enter_mounted(m, f);
- else
- mount_enter_dead(m, f);
+ if (f == MOUNT_SUCCESS) {
+ /* Either /bin/mount has an unexpected definition of success,
+ * or someone raced us and we lost. */
+ log_unit_warning(UNIT(m), "Mount process finished, but there is no mount.");
+ f = MOUNT_FAILURE_PROTOCOL;
+ }
+ mount_enter_dead(m, f);
+ break;
+
+ case MOUNT_MOUNTING_DONE:
+ mount_enter_mounted(m, f);
break;
case MOUNT_REMOUNTING:
@@ -1302,28 +1304,31 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
break;
case MOUNT_UNMOUNTING:
- case MOUNT_UNMOUNTING_SIGKILL:
- case MOUNT_UNMOUNTING_SIGTERM:
- if (m->from_proc_self_mountinfo) {
+ if (f == MOUNT_SUCCESS && m->from_proc_self_mountinfo) {
/* Still a mount point? If so, let's try again. Most likely there were multiple mount points
- * stacked on top of each other. Note that due to the io event priority logic we can be sure
- * the new mountinfo is loaded before we process the SIGCHLD for the mount command. */
+ * stacked on top of each other. We might exceed the timeout specified by the user overall,
+ * but we will stop as soon as any one umount times out. */
if (m->n_retry_umount < RETRY_UMOUNT_MAX) {
log_unit_debug(u, "Mount still present, trying again.");
m->n_retry_umount++;
mount_enter_unmounting(m);
} else {
- log_unit_debug(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount);
+ log_unit_warning(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount);
mount_enter_mounted(m, f);
}
} else
- mount_enter_dead(m, f);
+ mount_enter_dead_or_mounted(m, f);
break;
+ case MOUNT_UNMOUNTING_SIGKILL:
+ case MOUNT_UNMOUNTING_SIGTERM:
+ mount_enter_dead_or_mounted(m, f);
+ break;
+
default:
assert_not_reached("Uh, control process died at wrong time.");
}
@@ -1486,7 +1491,7 @@ static int mount_setup_existing_unit(
flags->just_changed = r1 > 0 || r2 > 0 || r3 > 0;
flags->is_mounted = true;
- flags->just_mounted = !MOUNT(u)->from_proc_self_mountinfo;
+ flags->just_mounted = !MOUNT(u)->from_proc_self_mountinfo || MOUNT(u)->just_mounted;
MOUNT(u)->from_proc_self_mountinfo = true;
@@ -1748,7 +1753,7 @@ static void mount_enumerate(Manager *m) {
goto fail;
}
- r = sd_event_source_set_priority(m->mount_event_source, -10);
+ r = sd_event_source_set_priority(m->mount_event_source, SD_EVENT_PRIORITY_NORMAL-10);
if (r < 0) {
log_error_errno(r, "Failed to adjust mount watch priority: %m");
goto fail;
@@ -1947,6 +1952,7 @@ static const char* const mount_result_table[_MOUNT_RESULT_MAX] = {
[MOUNT_FAILURE_SIGNAL] = "signal",
[MOUNT_FAILURE_CORE_DUMP] = "core-dump",
[MOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
+ [MOUNT_FAILURE_PROTOCOL] = "protocol",
};
DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult);
diff --git a/src/core/mount.h b/src/core/mount.h
index 44fe3b889e..1a496def84 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -35,12 +35,13 @@ typedef enum MountExecCommand {
typedef enum MountResult {
MOUNT_SUCCESS,
- MOUNT_FAILURE_RESOURCES,
+ MOUNT_FAILURE_RESOURCES, /* a bit of a misnomer, just our catch-all error for errnos we didn't expect */
MOUNT_FAILURE_TIMEOUT,
MOUNT_FAILURE_EXIT_CODE,
MOUNT_FAILURE_SIGNAL,
MOUNT_FAILURE_CORE_DUMP,
MOUNT_FAILURE_START_LIMIT_HIT,
+ MOUNT_FAILURE_PROTOCOL,
_MOUNT_RESULT_MAX,
_MOUNT_RESULT_INVALID = -1
} MountResult;
diff --git a/src/core/namespace.c b/src/core/namespace.c
index a3262fcc4d..70089f212a 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -42,6 +42,7 @@
#include "path-util.h"
#include "selinux-util.h"
#include "socket-util.h"
+#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
@@ -496,6 +497,35 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, unsigne
*n = t - m;
}
+static int clone_device_node(const char *d, const char *temporary_mount) {
+ const char *dn;
+ struct stat st;
+ int r;
+
+ if (stat(d, &st) < 0) {
+ if (errno == ENOENT)
+ return 0;
+ return -errno;
+ }
+
+ if (!S_ISBLK(st.st_mode) &&
+ !S_ISCHR(st.st_mode))
+ return -EINVAL;
+
+ if (st.st_rdev == 0)
+ return 0;
+
+ dn = strjoina(temporary_mount, d);
+
+ mac_selinux_create_file_prepare(d, st.st_mode);
+ r = mknod(dn, st.st_mode, st.st_rdev);
+ mac_selinux_create_file_clear();
+ if (r < 0)
+ return log_debug_errno(errno, "mknod failed for %s: %m", d);
+
+ return 1;
+}
+
static int mount_private_dev(MountEntry *m) {
static const char devnodes[] =
"/dev/null\0"
@@ -531,14 +561,33 @@ static int mount_private_dev(MountEntry *m) {
goto fail;
}
- devptmx = strjoina(temporary_mount, "/dev/ptmx");
- if (symlink("pts/ptmx", devptmx) < 0) {
- r = -errno;
+ /* /dev/ptmx can either be a device node or a symlink to /dev/pts/ptmx
+ * when /dev/ptmx a device node, /dev/pts/ptmx has 000 permissions making it inaccessible
+ * thus, in that case make a clone
+ *
+ * in nspawn and other containers it will be a symlink, in that case make it a symlink
+ */
+ r = is_symlink("/dev/ptmx");
+ if (r < 0)
goto fail;
+ if (r > 0) {
+ devptmx = strjoina(temporary_mount, "/dev/ptmx");
+ if (symlink("pts/ptmx", devptmx) < 0) {
+ r = -errno;
+ goto fail;
+ }
+ } else {
+ r = clone_device_node("/dev/ptmx", temporary_mount);
+ if (r < 0)
+ goto fail;
+ if (r == 0) {
+ r = -ENXIO;
+ goto fail;
+ }
}
devshm = strjoina(temporary_mount, "/dev/shm");
- (void) mkdir(devshm, 01777);
+ (void) mkdir(devshm, 0755);
r = mount("/dev/shm", devshm, NULL, MS_BIND, NULL);
if (r < 0) {
r = -errno;
@@ -557,42 +606,9 @@ static int mount_private_dev(MountEntry *m) {
(void) symlink("/run/systemd/journal/dev-log", devlog);
NULSTR_FOREACH(d, devnodes) {
- _cleanup_free_ char *dn = NULL;
- struct stat st;
-
- r = stat(d, &st);
- if (r < 0) {
-
- if (errno == ENOENT)
- continue;
-
- r = -errno;
- goto fail;
- }
-
- if (!S_ISBLK(st.st_mode) &&
- !S_ISCHR(st.st_mode)) {
- r = -EINVAL;
- goto fail;
- }
-
- if (st.st_rdev == 0)
- continue;
-
- dn = strappend(temporary_mount, d);
- if (!dn) {
- r = -ENOMEM;
- goto fail;
- }
-
- mac_selinux_create_file_prepare(d, st.st_mode);
- r = mknod(dn, st.st_mode, st.st_rdev);
- mac_selinux_create_file_clear();
-
- if (r < 0) {
- r = -errno;
+ r = clone_device_node(d, temporary_mount);
+ if (r < 0)
goto fail;
- }
}
dev_setup(temporary_mount, UID_INVALID, GID_INVALID);
@@ -1450,6 +1466,18 @@ static const char *const protect_home_table[_PROTECT_HOME_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(protect_home, ProtectHome);
+ProtectHome parse_protect_home_or_bool(const char *s) {
+ int r;
+
+ r = parse_boolean(s);
+ if (r > 0)
+ return PROTECT_HOME_YES;
+ if (r == 0)
+ return PROTECT_HOME_NO;
+
+ return protect_home_from_string(s);
+}
+
static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
[PROTECT_SYSTEM_NO] = "no",
[PROTECT_SYSTEM_YES] = "yes",
@@ -1459,6 +1487,18 @@ static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem);
+ProtectSystem parse_protect_system_or_bool(const char *s) {
+ int r;
+
+ r = parse_boolean(s);
+ if (r > 0)
+ return PROTECT_SYSTEM_YES;
+ if (r == 0)
+ return PROTECT_SYSTEM_NO;
+
+ return protect_system_from_string(s);
+}
+
static const char* const namespace_type_table[] = {
[NAMESPACE_MOUNT] = "mnt",
[NAMESPACE_CGROUP] = "cgroup",
diff --git a/src/core/namespace.h b/src/core/namespace.h
index f0f198362c..42d841c4d2 100644
--- a/src/core/namespace.h
+++ b/src/core/namespace.h
@@ -101,9 +101,11 @@ int setup_netns(int netns_storage_socket[2]);
const char* protect_home_to_string(ProtectHome p) _const_;
ProtectHome protect_home_from_string(const char *s) _pure_;
+ProtectHome parse_protect_home_or_bool(const char *s);
const char* protect_system_to_string(ProtectSystem p) _const_;
ProtectSystem protect_system_from_string(const char *s) _pure_;
+ProtectSystem parse_protect_system_or_bool(const char *s);
void bind_mount_free_many(BindMount *b, unsigned n);
int bind_mount_add(BindMount **b, unsigned *n, const BindMount *item);
diff --git a/src/core/path.c b/src/core/path.c
index 6b22451a08..8a5ec0a72f 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -772,6 +772,9 @@ const UnitVTable path_vtable = {
"Unit\0"
"Path\0"
"Install\0",
+ .private_section = "Path",
+
+ .can_transient = true,
.init = path_init,
.done = path_done,
@@ -794,5 +797,6 @@ const UnitVTable path_vtable = {
.reset_failed = path_reset_failed,
- .bus_vtable = bus_path_vtable
+ .bus_vtable = bus_path_vtable,
+ .bus_set_property = bus_path_set_property,
};
diff --git a/src/core/scope.c b/src/core/scope.c
index 10454d56b0..468dd81217 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -472,19 +472,16 @@ static void scope_notify_cgroup_empty_event(Unit *u) {
static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) {
- /* If we get a SIGCHLD event for one of the processes we were
- interested in, then we look for others to watch, under the
- assumption that we'll sooner or later get a SIGCHLD for
- them, as the original process we watched was probably the
- parent of them, and they are hence now our children. */
+ assert(u);
+ /* If we get a SIGCHLD event for one of the processes we were interested in, then we look for others to
+ * watch, under the assumption that we'll sooner or later get a SIGCHLD for them, as the original
+ * process we watched was probably the parent of them, and they are hence now our children. */
unit_tidy_watch_pids(u, 0, 0);
unit_watch_all_pids(u);
- /* If the PID set is empty now, then let's finish this off
- (On unified we use proper notifications) */
- if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) == 0 && set_isempty(u->pids))
- scope_notify_cgroup_empty_event(u);
+ /* If the PID set is empty now, then let's finish this off. */
+ unit_synthesize_cgroup_empty_event(u);
}
static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) {
diff --git a/src/core/service.c b/src/core/service.c
index ef1be33260..6476dc68c5 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -866,9 +866,45 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
cgroup_context_dump(&s->cgroup_context, f, prefix);
}
+static int service_is_suitable_main_pid(Service *s, pid_t pid, int prio) {
+ Unit *owner;
+
+ assert(s);
+ assert(pid_is_valid(pid));
+
+ /* Checks whether the specified PID is suitable as main PID for this service. returns negative if not, 0 if the
+ * PID is questionnable but should be accepted if the source of configuration is trusted. > 0 if the PID is
+ * good */
+
+ if (pid == getpid_cached() || pid == 1) {
+ log_unit_full(UNIT(s), prio, 0, "New main PID "PID_FMT" is the manager, refusing.", pid);
+ return -EPERM;
+ }
+
+ if (pid == s->control_pid) {
+ log_unit_full(UNIT(s), prio, 0, "New main PID "PID_FMT" is the control process, refusing.", pid);
+ return -EPERM;
+ }
+
+ if (!pid_is_alive(pid)) {
+ log_unit_full(UNIT(s), prio, 0, "New main PID "PID_FMT" does not exist or is a zombie.", pid);
+ return -ESRCH;
+ }
+
+ owner = manager_get_unit_by_pid(UNIT(s)->manager, pid);
+ if (owner == UNIT(s)) {
+ log_unit_debug(UNIT(s), "New main PID "PID_FMT" belongs to service, we are happy.", pid);
+ return 1; /* Yay, it's definitely a good PID */
+ }
+
+ return 0; /* Hmm it's a suspicious PID, let's accept it if configuration source is trusted */
+}
+
static int service_load_pid_file(Service *s, bool may_warn) {
+ char procfs[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *k = NULL;
- int r;
+ _cleanup_close_ int fd = -1;
+ int r, prio;
pid_t pid;
assert(s);
@@ -876,30 +912,47 @@ static int service_load_pid_file(Service *s, bool may_warn) {
if (!s->pid_file)
return -ENOENT;
- r = read_one_line_file(s->pid_file, &k);
- if (r < 0) {
- if (may_warn)
- log_unit_info_errno(UNIT(s), r, "PID file %s not readable (yet?) after %s: %m", s->pid_file, service_state_to_string(s->state));
- return r;
- }
+ prio = may_warn ? LOG_INFO : LOG_DEBUG;
+
+ fd = chase_symlinks(s->pid_file, NULL, CHASE_OPEN|CHASE_SAFE, NULL);
+ if (fd == -EPERM)
+ return log_unit_full(UNIT(s), prio, fd, "Permission denied while opening PID file or unsafe symlink chain: %s", s->pid_file);
+ if (fd < 0)
+ return log_unit_full(UNIT(s), prio, fd, "Can't open PID file %s (yet?) after %s: %m", s->pid_file, service_state_to_string(s->state));
+
+ /* Let's read the PID file now that we chased it down. But we need to convert the O_PATH fd chase_symlinks() returned us into a proper fd first. */
+ xsprintf(procfs, "/proc/self/fd/%i", fd);
+ r = read_one_line_file(procfs, &k);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Can't convert PID files %s O_PATH file descriptor to proper file descriptor: %m", s->pid_file);
r = parse_pid(k, &pid);
- if (r < 0) {
- if (may_warn)
- log_unit_info_errno(UNIT(s), r, "Failed to read PID from file %s: %m", s->pid_file);
+ if (r < 0)
+ return log_unit_full(UNIT(s), prio, r, "Failed to parse PID from file %s: %m", s->pid_file);
+
+ if (s->main_pid_known && pid == s->main_pid)
+ return 0;
+
+ r = service_is_suitable_main_pid(s, pid, prio);
+ if (r < 0)
return r;
- }
+ if (r == 0) {
+ struct stat st;
- if (!pid_is_alive(pid)) {
- if (may_warn)
- log_unit_info(UNIT(s), "PID "PID_FMT" read from file %s does not exist or is a zombie.", pid, s->pid_file);
- return -ESRCH;
+ /* Hmm, it's not clear if the new main PID is safe. Let's allow this if the PID file is owned by root */
+
+ if (fstat(fd, &st) < 0)
+ return log_unit_error_errno(UNIT(s), errno, "Failed to fstat() PID file O_PATH fd: %m");
+
+ if (st.st_uid != 0) {
+ log_unit_error(UNIT(s), "New main PID "PID_FMT" does not belong to service, and PID file is not owned by root. Refusing.", pid);
+ return -EPERM;
+ }
+
+ log_unit_debug(UNIT(s), "New main PID "PID_FMT" does not belong to service, but we'll accept it since PID file is owned by root.", pid);
}
if (s->main_pid_known) {
- if (pid == s->main_pid)
- return 0;
-
log_unit_debug(UNIT(s), "Main PID changing: "PID_FMT" -> "PID_FMT, s->main_pid, pid);
service_unwatch_main_pid(s);
@@ -915,7 +968,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
if (r < 0) /* FIXME: we need to do something here */
return log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid);
- return 0;
+ return 1;
}
static void service_search_main_pid(Service *s) {
@@ -1007,26 +1060,6 @@ static void service_set_state(Service *s, ServiceState state) {
if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(UNIT(s)->manager))
unit_prune_cgroup(UNIT(s));
- /* For remain_after_exit services, let's see if we can "release" the
- * hold on the console, since unit_notify() only does that in case of
- * change of state */
- if (state == SERVICE_EXITED &&
- s->remain_after_exit &&
- UNIT(s)->manager->n_on_console > 0) {
-
- ExecContext *ec;
-
- ec = unit_get_exec_context(UNIT(s));
- if (ec && exec_context_may_touch_console(ec)) {
- Manager *m = UNIT(s)->manager;
-
- m->n_on_console--;
- if (m->n_on_console == 0)
- /* unset no_console_output flag, since the console is free */
- m->no_console_output = false;
- }
- }
-
if (old_state != state)
log_unit_debug(UNIT(s), "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state));
@@ -1080,8 +1113,7 @@ static int service_coldplug(Unit *u) {
if (s->main_pid > 0 &&
pid_is_unwaited(s->main_pid) &&
- ((s->deserialized_state == SERVICE_START && IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_ONESHOT, SERVICE_NOTIFY)) ||
- IN_SET(s->deserialized_state,
+ (IN_SET(s->deserialized_state,
SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING, SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
@@ -2586,10 +2618,8 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
if (parse_pid(value, &pid) < 0)
log_unit_debug(u, "Failed to parse main-pid value: %s", value);
- else {
- service_set_main_pid(s, pid);
- unit_watch_pid(UNIT(s), pid);
- }
+ else
+ (void) service_set_main_pid(s, pid);
} else if (streq(key, "main-pid-known")) {
int b;
@@ -2960,6 +2990,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
}
static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
+ bool notify_dbus = true;
Service *s = SERVICE(u);
ServiceResult f;
@@ -2981,7 +3012,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* Forking services may occasionally move to a new PID.
* As long as they update the PID file before exiting the old
* PID, they're fine. */
- if (service_load_pid_file(s, false) == 0)
+ if (service_load_pid_file(s, false) > 0)
return;
s->main_pid = 0;
@@ -3238,23 +3269,21 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
assert_not_reached("Uh, control process died at wrong time.");
}
}
- }
+ } else /* Neither control nor main PID? If so, don't notify about anything */
+ notify_dbus = false;
/* Notify clients about changed exit status */
- unit_add_to_dbus_queue(u);
+ if (notify_dbus)
+ unit_add_to_dbus_queue(u);
- /* We got one SIGCHLD for the service, let's watch all
- * processes that are now running of the service, and watch
- * that. Among the PIDs we then watch will be children
- * reassigned to us, which hopefully allows us to identify
- * when all children are gone */
+ /* If we get a SIGCHLD event for one of the processes we were interested in, then we look for others to watch,
+ * under the assumption that we'll sooner or later get a SIGCHLD for them, as the original process we watched
+ * was probably the parent of them, and they are hence now our children. */
unit_tidy_watch_pids(u, s->main_pid, s->control_pid);
unit_watch_all_pids(u);
- /* If the PID set is empty now, then let's finish this off
- (On unified we use proper notifications) */
- if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) == 0 && set_isempty(u->pids))
- unit_add_to_cgroup_empty_queue(u);
+ /* If the PID set is empty now, then let's check if the cgroup is empty too and finish off the unit. */
+ unit_synthesize_cgroup_empty_event(u);
}
static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) {
@@ -3364,10 +3393,14 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void
watchdog_usec = service_get_watchdog_usec(s);
- log_unit_error(UNIT(s), "Watchdog timeout (limit %s)!",
- format_timespan(t, sizeof(t), watchdog_usec, 1));
+ if (UNIT(s)->manager->service_watchdogs) {
+ log_unit_error(UNIT(s), "Watchdog timeout (limit %s)!",
+ format_timespan(t, sizeof(t), watchdog_usec, 1));
- service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG);
+ service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG);
+ } else
+ log_unit_warning(UNIT(s), "Watchdog disabled! Ignoring watchdog timeout (limit %s)!",
+ format_timespan(t, sizeof(t), watchdog_usec, 1));
return 0;
}
@@ -3406,37 +3439,55 @@ static bool service_notify_message_authorized(Service *s, pid_t pid, char **tags
return true;
}
-static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds) {
+static void service_notify_message(
+ Unit *u,
+ const struct ucred *ucred,
+ char **tags,
+ FDSet *fds) {
+
Service *s = SERVICE(u);
bool notify_dbus = false;
const char *e;
char **i;
+ int r;
assert(u);
+ assert(ucred);
- if (!service_notify_message_authorized(SERVICE(u), pid, tags, fds))
+ if (!service_notify_message_authorized(SERVICE(u), ucred->pid, tags, fds))
return;
- if (log_get_max_level() >= LOG_DEBUG) {
+ if (DEBUG_LOGGING) {
_cleanup_free_ char *cc = NULL;
cc = strv_join(tags, ", ");
- log_unit_debug(u, "Got notification message from PID "PID_FMT" (%s)", pid, isempty(cc) ? "n/a" : cc);
+ log_unit_debug(u, "Got notification message from PID "PID_FMT" (%s)", ucred->pid, isempty(cc) ? "n/a" : cc);
}
/* Interpret MAINPID= */
e = strv_find_startswith(tags, "MAINPID=");
if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) {
- if (parse_pid(e, &pid) < 0)
- log_unit_warning(u, "Failed to parse MAINPID= field in notification message: %s", e);
- else if (pid == s->control_pid)
- log_unit_warning(u, "A control process cannot also be the main process");
- else if (pid == getpid_cached() || pid == 1)
- log_unit_warning(u, "Service manager can't be main process, ignoring sd_notify() MAINPID= field");
- else if (pid != s->main_pid) {
- service_set_main_pid(s, pid);
- unit_watch_pid(UNIT(s), pid);
- notify_dbus = true;
+ pid_t new_main_pid;
+
+ if (parse_pid(e, &new_main_pid) < 0)
+ log_unit_warning(u, "Failed to parse MAINPID= field in notification message, ignoring: %s", e);
+ else if (!s->main_pid_known || new_main_pid != s->main_pid) {
+
+ r = service_is_suitable_main_pid(s, new_main_pid, LOG_WARNING);
+ if (r == 0) {
+ /* The new main PID is a bit suspicous, which is OK if the sender is privileged. */
+
+ if (ucred->uid == 0) {
+ log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, but we'll accept it as the request to change it came from a privileged process.", new_main_pid);
+ r = 1;
+ } else
+ log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid);
+ }
+ if (r > 0) {
+ service_set_main_pid(s, new_main_pid);
+ unit_watch_pid(UNIT(s), new_main_pid);
+ notify_dbus = true;
+ }
}
}
@@ -3735,6 +3786,32 @@ static int service_control_pid(Unit *u) {
return s->control_pid;
}
+static bool service_needs_console(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ /* We provide our own implementation of this here, instead of relying of the generic implementation
+ * unit_needs_console() provides, since we want to return false if we are in SERVICE_EXITED state. */
+
+ if (!exec_context_may_touch_console(&s->exec_context))
+ return false;
+
+ return IN_SET(s->state,
+ SERVICE_START_PRE,
+ SERVICE_START,
+ SERVICE_START_POST,
+ SERVICE_RUNNING,
+ SERVICE_RELOAD,
+ SERVICE_STOP,
+ SERVICE_STOP_SIGABRT,
+ SERVICE_STOP_SIGTERM,
+ SERVICE_STOP_SIGKILL,
+ SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM,
+ SERVICE_FINAL_SIGKILL);
+}
+
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
@@ -3850,6 +3927,7 @@ const UnitVTable service_vtable = {
.bus_commit_properties = bus_service_commit_properties,
.get_timeout = service_get_timeout,
+ .needs_console = service_needs_console,
.can_transient = true,
.status_message_formats = {
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index aca89d13d1..cc31b33f1c 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -31,10 +31,11 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "async.h"
#include "cgroup-util.h"
-#include "fd-util.h"
#include "def.h"
#include "exec-util.h"
+#include "fd-util.h"
#include "fileio.h"
#include "killall.h"
#include "log.h"
@@ -57,6 +58,7 @@
static char* arg_verb;
static uint8_t arg_exit_code;
+static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
static int parse_argv(int argc, char *argv[]) {
enum {
@@ -65,6 +67,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_LOG_COLOR,
ARG_LOG_LOCATION,
ARG_EXIT_CODE,
+ ARG_TIMEOUT,
};
static const struct option options[] = {
@@ -73,6 +76,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "log-color", optional_argument, NULL, ARG_LOG_COLOR },
{ "log-location", optional_argument, NULL, ARG_LOG_LOCATION },
{ "exit-code", required_argument, NULL, ARG_EXIT_CODE },
+ { "timeout", required_argument, NULL, ARG_TIMEOUT },
{}
};
@@ -128,6 +132,13 @@ static int parse_argv(int argc, char *argv[]) {
break;
+ case ARG_TIMEOUT:
+ r = parse_sec(optarg, &arg_timeout);
+ if (r < 0)
+ log_error("Failed to parse shutdown timeout %s, ignoring", optarg);
+
+ break;
+
case '\001':
if (!arg_verb)
arg_verb = optarg;
@@ -211,26 +222,20 @@ static bool sync_making_progress(unsigned long long *prev_dirty) {
}
static void sync_with_progress(void) {
+ unsigned long long dirty = ULONG_LONG_MAX;
unsigned checks;
pid_t pid;
int r;
- unsigned long long dirty = ULONG_LONG_MAX;
BLOCK_SIGNALS(SIGCHLD);
- /* Due to the possiblity of the sync operation hanging, we fork
- * a child process and monitor the progress. If the timeout
- * lapses, the assumption is that that particular sync stalled. */
- pid = fork();
- if (pid < 0) {
- log_error_errno(errno, "Failed to fork: %m");
- return;
- }
+ /* Due to the possiblity of the sync operation hanging, we fork a child process and monitor the progress. If
+ * the timeout lapses, the assumption is that that particular sync stalled. */
- if (pid == 0) {
- /* Start the sync operation here in the child */
- sync();
- _exit(EXIT_SUCCESS);
+ r = asynchronous_sync(&pid);
+ if (r < 0) {
+ log_error_errno(r, "Failed to fork sync(): %m");
+ return;
}
log_info("Syncing filesystems and block devices.");
@@ -279,7 +284,7 @@ int main(int argc, char *argv[]) {
/* journald will die if not gone yet. The log target defaults
* to console, but may have been changed by command line options. */
- log_close_console(); /* force reopen of /dev/console */
+ log_set_prohibit_ipc(true);
log_open();
umask(0022);
@@ -328,11 +333,13 @@ int main(int argc, char *argv[]) {
if (!in_container)
sync_with_progress();
+ disable_coredumps();
+
log_info("Sending SIGTERM to remaining processes...");
- broadcast_signal(SIGTERM, true, true);
+ broadcast_signal(SIGTERM, true, true, arg_timeout);
log_info("Sending SIGKILL to remaining processes...");
- broadcast_signal(SIGKILL, true, false);
+ broadcast_signal(SIGKILL, true, false, arg_timeout);
need_umount = !in_container;
need_swapoff = !in_container;
@@ -488,15 +495,10 @@ int main(int argc, char *argv[]) {
if (!in_container) {
/* We cheat and exec kexec to avoid doing all its work */
- pid_t pid;
-
log_info("Rebooting with kexec.");
- pid = fork();
- if (pid < 0)
- log_error_errno(errno, "Failed to fork: %m");
- else if (pid == 0) {
-
+ r = safe_fork("(sd-kexec)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, NULL);
+ if (r == 0) {
const char * const args[] = {
KEXEC, "-e", NULL
};
@@ -505,8 +507,9 @@ int main(int argc, char *argv[]) {
execv(args[0], (char * const *) args);
_exit(EXIT_FAILURE);
- } else
- wait_for_terminate_and_warn("kexec", pid, true);
+ }
+
+ /* If we are still running, then the kexec can't have worked, let's fall through */
}
cmd = RB_AUTOBOOT;
@@ -548,7 +551,7 @@ int main(int argc, char *argv[]) {
* CAP_SYS_BOOT just exit, this will kill our
* container for good. */
log_info("Exiting container.");
- exit(0);
+ exit(EXIT_SUCCESS);
}
r = log_error_errno(errno, "Failed to invoke reboot(): %m");
diff --git a/src/core/slice.c b/src/core/slice.c
index 5ab1e6f898..fef47b04fe 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -59,30 +59,24 @@ static void slice_set_state(Slice *t, SliceState state) {
}
static int slice_add_parent_slice(Slice *s) {
- char *a, *dash;
- Unit *parent;
+ Unit *u = UNIT(s), *parent;
+ _cleanup_free_ char *a = NULL;
int r;
assert(s);
- if (UNIT_ISSET(UNIT(s)->slice))
+ if (UNIT_ISSET(u->slice))
return 0;
- if (unit_has_name(UNIT(s), SPECIAL_ROOT_SLICE))
- return 0;
-
- a = strdupa(UNIT(s)->id);
- dash = strrchr(a, '-');
- if (dash)
- strcpy(dash, ".slice");
- else
- a = (char*) SPECIAL_ROOT_SLICE;
+ r = slice_build_parent_slice(u->id, &a);
+ if (r <= 0) /* 0 means root slice */
+ return r;
- r = manager_load_unit(UNIT(s)->manager, a, NULL, NULL, &parent);
+ r = manager_load_unit(u->manager, a, NULL, NULL, &parent);
if (r < 0)
return r;
- unit_ref_set(&UNIT(s)->slice, parent);
+ unit_ref_set(&u->slice, parent);
return 0;
}
diff --git a/src/core/socket.c b/src/core/socket.c
index 7e3630ada7..74cdebbe81 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -53,6 +53,7 @@
#include "signal-util.h"
#include "smack-util.h"
#include "socket.h"
+#include "socket-protocol-list.h"
#include "special.h"
#include "string-table.h"
#include "string-util.h"
@@ -655,7 +656,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
SocketExecCommand c;
Socket *s = SOCKET(u);
SocketPort *p;
- const char *prefix2;
+ const char *prefix2, *str;
assert(s);
assert(f);
@@ -680,7 +681,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%sTCPCongestion: %s\n"
"%sRemoveOnStop: %s\n"
"%sWritable: %s\n"
- "%sFDName: %s\n"
+ "%sFileDescriptorName: %s\n"
"%sSELinuxContextFromNet: %s\n",
prefix, socket_state_to_string(s->state),
prefix, socket_result_to_string(s->result),
@@ -715,10 +716,12 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
fprintf(f,
"%sAccepted: %u\n"
"%sNConnections: %u\n"
- "%sMaxConnections: %u\n",
+ "%sMaxConnections: %u\n"
+ "%sMaxConnectionsPerSource: %u\n",
prefix, s->n_accepted,
prefix, s->n_connections,
- prefix, s->max_connections);
+ prefix, s->max_connections,
+ prefix, s->max_connections_per_source);
if (s->priority >= 0)
fprintf(f,
@@ -843,6 +846,24 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->trigger_limit.interval, USEC_PER_SEC),
prefix, s->trigger_limit.burst);
+ str = socket_protocol_to_name(s->socket_protocol);
+ if (str)
+ fprintf(f, "%sSocketProtocol: %s\n", prefix, str);
+
+ if (!strv_isempty(s->symlinks)) {
+ char **q;
+
+ fprintf(f, "%sSymlinks:", prefix);
+ STRV_FOREACH(q, s->symlinks)
+ fprintf(f, " %s", *q);
+
+ fprintf(f, "\n");
+ }
+
+ fprintf(f,
+ "%sTimeoutSec: %s\n",
+ prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->timeout_usec, USEC_PER_SEC));
+
exec_context_dump(&s->exec_context, f, prefix);
kill_context_dump(&s->kill_context, f, prefix);
@@ -1506,7 +1527,7 @@ static int socket_address_listen_in_cgroup(
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0)
return log_unit_error_errno(UNIT(s), errno, "Failed to create communication channel: %m");
- r = unit_fork_helper_process(UNIT(s), &pid);
+ r = unit_fork_helper_process(UNIT(s), "(sd-listen)", &pid);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to fork off listener stub process: %m");
if (r == 0) {
@@ -1533,7 +1554,7 @@ static int socket_address_listen_in_cgroup(
fd = receive_one_fd(pair[0], 0);
/* We synchronously wait for the helper, as it shouldn't be slow */
- r = wait_for_terminate_and_warn("listen-cgroup-helper", pid, false);
+ r = wait_for_terminate_and_check("(sd-listen)", pid, WAIT_LOG_ABNORMAL);
if (r < 0) {
safe_close(fd);
return r;
@@ -1923,7 +1944,7 @@ static int socket_chown(Socket *s, pid_t *_pid) {
/* We have to resolve the user names out-of-process, hence
* let's fork here. It's messy, but well, what can we do? */
- r = unit_fork_helper_process(UNIT(s), &pid);
+ r = unit_fork_helper_process(UNIT(s), "(sd-chown)", &pid);
if (r < 0)
return r;
if (r == 0) {
@@ -2780,6 +2801,23 @@ const char* socket_port_type_to_string(SocketPort *p) {
}
}
+SocketType socket_port_type_from_string(const char *s) {
+ assert(s);
+
+ if (STR_IN_SET(s, "Stream", "Datagram", "SequentialPacket", "Netlink"))
+ return SOCKET_SOCKET;
+ else if (streq(s, "Special"))
+ return SOCKET_SPECIAL;
+ else if (streq(s, "MessageQueue"))
+ return SOCKET_MQUEUE;
+ else if (streq(s, "FIFO"))
+ return SOCKET_FIFO;
+ else if (streq(s, "USBFunction"))
+ return SOCKET_USB_FUNCTION;
+ else
+ return _SOCKET_TYPE_INVALID;
+}
+
_pure_ static bool socket_check_gc(Unit *u) {
Socket *s = SOCKET(u);
@@ -2833,7 +2871,7 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) {
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0)
return log_unit_error_errno(UNIT(s), errno, "Failed to create communication channel: %m");
- r = unit_fork_helper_process(UNIT(s), &pid);
+ r = unit_fork_helper_process(UNIT(s), "(sd-accept)", &pid);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to fork off accept stub process: %m");
if (r == 0) {
@@ -2860,7 +2898,7 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) {
cfd = receive_one_fd(pair[0], 0);
/* We synchronously wait for the helper, as it shouldn't be slow */
- r = wait_for_terminate_and_warn("accept-cgroup-helper", pid, false);
+ r = wait_for_terminate_and_check("(sd-accept)", pid, WAIT_LOG_ABNORMAL);
if (r < 0) {
safe_close(cfd);
return r;
@@ -3212,10 +3250,7 @@ char *socket_fdname(Socket *s) {
* didn't specify anything specifically, use the socket unit's
* name as fallback. */
- if (s->fdname)
- return s->fdname;
-
- return UNIT(s)->id;
+ return s->fdname ?: UNIT(s)->id;
}
static int socket_control_pid(Unit *u) {
@@ -3227,11 +3262,11 @@ static int socket_control_pid(Unit *u) {
}
static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
- [SOCKET_EXEC_START_PRE] = "StartPre",
- [SOCKET_EXEC_START_CHOWN] = "StartChown",
- [SOCKET_EXEC_START_POST] = "StartPost",
- [SOCKET_EXEC_STOP_PRE] = "StopPre",
- [SOCKET_EXEC_STOP_POST] = "StopPost"
+ [SOCKET_EXEC_START_PRE] = "ExecStartPre",
+ [SOCKET_EXEC_START_CHOWN] = "ExecStartChown",
+ [SOCKET_EXEC_START_POST] = "ExecStartPost",
+ [SOCKET_EXEC_STOP_PRE] = "ExecStopPre",
+ [SOCKET_EXEC_STOP_POST] = "ExecStopPost"
};
DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand);
@@ -3264,6 +3299,8 @@ const UnitVTable socket_vtable = {
"Install\0",
.private_section = "Socket",
+ .can_transient = true,
+
.init = socket_init,
.done = socket_done,
.load = socket_load,
diff --git a/src/core/socket.h b/src/core/socket.h
index e9e560e57e..9c528fb39c 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -43,8 +43,8 @@ typedef enum SocketType {
SOCKET_SPECIAL,
SOCKET_MQUEUE,
SOCKET_USB_FUNCTION,
- _SOCKET_FIFO_MAX,
- _SOCKET_FIFO_INVALID = -1
+ _SOCKET_TYPE_MAX,
+ _SOCKET_TYPE_INVALID = -1
} SocketType;
typedef enum SocketResult {
@@ -194,3 +194,4 @@ const char* socket_result_to_string(SocketResult i) _const_;
SocketResult socket_result_from_string(const char *s) _pure_;
const char* socket_port_type_to_string(SocketPort *p) _pure_;
+SocketType socket_port_type_from_string(const char *p) _pure_;
diff --git a/src/core/swap.c b/src/core/swap.c
index 849ccbdd43..70097ff2ba 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -1309,7 +1309,7 @@ static void swap_enumerate(Manager *m) {
/* Dispatch this before we dispatch SIGCHLD, so that
* we always get the events from /proc/swaps before
* the SIGCHLD of /sbin/swapon. */
- r = sd_event_source_set_priority(m->swap_event_source, -10);
+ r = sd_event_source_set_priority(m->swap_event_source, SD_EVENT_PRIORITY_NORMAL-10);
if (r < 0) {
log_error_errno(r, "Failed to change /proc/swaps priority: %m");
goto fail;
diff --git a/src/core/timer.c b/src/core/timer.c
index 03935eea94..133cbb974d 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -431,6 +431,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (base <= 0)
continue;
+ base = MAX(base, t->last_trigger.monotonic);
break;
@@ -443,6 +444,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (base <= 0)
continue;
+ base = MAX(base, t->last_trigger.monotonic);
break;
diff --git a/src/core/umount.c b/src/core/umount.c
index 7f8ddb99ee..731436af27 100644
--- a/src/core/umount.c
+++ b/src/core/umount.c
@@ -28,6 +28,7 @@
#include "libudev.h"
#include "alloc-util.h"
+#include "blockdev-util.h"
#include "def.h"
#include "escape.h"
#include "fd-util.h"
@@ -35,12 +36,13 @@
#include "linux-3.13/dm-ioctl.h"
#include "list.h"
#include "mount-setup.h"
+#include "mount-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "signal-util.h"
#include "string-util.h"
#include "udev-util.h"
#include "umount.h"
-#include "mount-util.h"
#include "util.h"
#include "virt.h"
@@ -388,11 +390,10 @@ static int remount_with_timeout(MountPoint *m, char *options, int *n_failed) {
* fork a child process and set a timeout. If the timeout
* lapses, the assumption is that that particular remount
* failed. */
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
-
- if (pid == 0) {
+ r = safe_fork("(sd-remount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
log_info("Remounting '%s' read-only in with options '%s'.", m->path, options);
/* Start the mount operation here in the child */
@@ -423,11 +424,10 @@ static int umount_with_timeout(MountPoint *m, bool *changed) {
* fork a child process and set a timeout. If the timeout
* lapses, the assumption is that that particular umount
* failed. */
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
-
- if (pid == 0) {
+ r = safe_fork("(sd-umount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
log_info("Unmounting '%s'.", m->path);
/* Start the mount operation here in the child Using MNT_FORCE
diff --git a/src/core/unit.c b/src/core/unit.c
index 7af8425707..932f05baa2 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -21,6 +21,7 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/prctl.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -629,6 +630,9 @@ void unit_free(Unit *u) {
if (u->in_cgroup_empty_queue)
LIST_REMOVE(cgroup_empty_queue, u->manager->cgroup_empty_queue, u);
+ if (u->on_console)
+ manager_unref_console(u->manager);
+
unit_release_cgroup(u);
if (!MANAGER_IS_RELOADING(u->manager))
@@ -2304,6 +2308,23 @@ finish:
}
+static void unit_update_on_console(Unit *u) {
+ bool b;
+
+ assert(u);
+
+ b = unit_needs_console(u);
+ if (u->on_console == b)
+ return;
+
+ u->on_console = b;
+ if (b)
+ manager_ref_console(u->manager);
+ else
+ manager_unref_console(u->manager);
+
+}
+
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
Manager *m;
bool unexpected;
@@ -2344,24 +2365,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
unit_unlink_state_files(u);
}
- /* Note that this doesn't apply to RemainAfterExit services exiting
- * successfully, since there's no change of state in that case. Which is
- * why it is handled in service_set_state() */
- if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) {
- ExecContext *ec;
-
- ec = unit_get_exec_context(u);
- if (ec && exec_context_may_touch_console(ec)) {
- if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
- m->n_on_console--;
-
- if (m->n_on_console == 0)
- /* unset no_console_output flag, since the console is free */
- m->no_console_output = false;
- } else
- m->n_on_console++;
- }
- }
+ unit_update_on_console(u);
if (u->job) {
unexpected = false;
@@ -2533,44 +2537,97 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
}
int unit_watch_pid(Unit *u, pid_t pid) {
- int q, r;
+ int r;
assert(u);
- assert(pid >= 1);
+ assert(pid_is_valid(pid));
- /* Watch a specific PID. We only support one or two units
- * watching each PID for now, not more. */
+ /* Watch a specific PID */
r = set_ensure_allocated(&u->pids, NULL);
if (r < 0)
return r;
- r = hashmap_ensure_allocated(&u->manager->watch_pids1, NULL);
+ r = hashmap_ensure_allocated(&u->manager->watch_pids, NULL);
if (r < 0)
return r;
- r = hashmap_put(u->manager->watch_pids1, PID_TO_PTR(pid), u);
- if (r == -EEXIST) {
- r = hashmap_ensure_allocated(&u->manager->watch_pids2, NULL);
- if (r < 0)
- return r;
+ /* First try, let's add the unit keyed by "pid". */
+ r = hashmap_put(u->manager->watch_pids, PID_TO_PTR(pid), u);
+ if (r == -EEXIST) {
+ Unit **array;
+ bool found = false;
+ size_t n = 0;
- r = hashmap_put(u->manager->watch_pids2, PID_TO_PTR(pid), u);
- }
+ /* OK, the "pid" key is already assigned to a different unit. Let's see if the "-pid" key (which points
+ * to an array of Units rather than just a Unit), lists us already. */
- q = set_put(u->pids, PID_TO_PTR(pid));
- if (q < 0)
- return q;
+ array = hashmap_get(u->manager->watch_pids, PID_TO_PTR(-pid));
+ if (array)
+ for (; array[n]; n++)
+ if (array[n] == u)
+ found = true;
- return r;
+ if (found) /* Found it already? if so, do nothing */
+ r = 0;
+ else {
+ Unit **new_array;
+
+ /* Allocate a new array */
+ new_array = new(Unit*, n + 2);
+ if (!new_array)
+ return -ENOMEM;
+
+ memcpy_safe(new_array, array, sizeof(Unit*) * n);
+ new_array[n] = u;
+ new_array[n+1] = NULL;
+
+ /* Add or replace the old array */
+ r = hashmap_replace(u->manager->watch_pids, PID_TO_PTR(-pid), new_array);
+ if (r < 0) {
+ free(new_array);
+ return r;
+ }
+
+ free(array);
+ }
+ } else if (r < 0)
+ return r;
+
+ r = set_put(u->pids, PID_TO_PTR(pid));
+ if (r < 0)
+ return r;
+
+ return 0;
}
void unit_unwatch_pid(Unit *u, pid_t pid) {
+ Unit **array;
+
assert(u);
- assert(pid >= 1);
+ assert(pid_is_valid(pid));
+
+ /* First let's drop the unit in case it's keyed as "pid". */
+ (void) hashmap_remove_value(u->manager->watch_pids, PID_TO_PTR(pid), u);
+
+ /* Then, let's also drop the unit, in case it's in the array keyed by -pid */
+ array = hashmap_get(u->manager->watch_pids, PID_TO_PTR(-pid));
+ if (array) {
+ size_t n, m = 0;
+
+ /* Let's iterate through the array, dropping our own entry */
+ for (n = 0; array[n]; n++)
+ if (array[n] != u)
+ array[m++] = array[n];
+ array[m] = NULL;
+
+ if (m == 0) {
+ /* The array is now empty, remove the entire entry */
+ assert(hashmap_remove(u->manager->watch_pids, PID_TO_PTR(-pid)) == array);
+ free(array);
+ }
+ }
- (void) hashmap_remove_value(u->manager->watch_pids1, PID_TO_PTR(pid), u);
- (void) hashmap_remove_value(u->manager->watch_pids2, PID_TO_PTR(pid), u);
(void) set_remove(u->pids, PID_TO_PTR(pid));
}
@@ -3041,7 +3098,7 @@ int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) {
"member='NameOwnerChanged',"
"arg0='", name, "'");
- return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u);
+ return sd_bus_add_match_async(bus, &u->match_bus_slot, match, signal_name_owner_changed, NULL, u);
}
int unit_watch_bus_name(Unit *u, const char *name) {
@@ -4704,25 +4761,29 @@ void unit_warn_if_dir_nonempty(Unit *u, const char* where) {
NULL);
}
-int unit_fail_if_symlink(Unit *u, const char* where) {
+int unit_fail_if_noncanonical(Unit *u, const char* where) {
+ _cleanup_free_ char *canonical_where;
int r;
assert(u);
assert(where);
- r = is_symlink(where);
+ r = chase_symlinks(where, NULL, CHASE_NONEXISTENT, &canonical_where);
if (r < 0) {
- log_unit_debug_errno(u, r, "Failed to check symlink %s, ignoring: %m", where);
+ log_unit_debug_errno(u, r, "Failed to check %s for symlinks, ignoring: %m", where);
return 0;
}
- if (r == 0)
+
+ /* We will happily ignore a trailing slash (or any redundant slashes) */
+ if (path_equal(where, canonical_where))
return 0;
+ /* No need to mention "." or "..", they would already have been rejected by unit_name_from_path() */
log_struct(LOG_ERR,
"MESSAGE_ID=" SD_MESSAGE_OVERMOUNTING_STR,
LOG_UNIT_ID(u),
LOG_UNIT_INVOCATION_ID(u),
- LOG_UNIT_MESSAGE(u, "Mount on symlink %s not allowed.", where),
+ LOG_UNIT_MESSAGE(u, "Mount path %s is not canonical (contains a symlink).", where),
"WHERE=%s", where,
NULL);
@@ -4968,8 +5029,7 @@ void unit_set_exec_params(Unit *u, ExecParameters *p) {
SET_FLAG(p->flags, EXEC_CGROUP_DELEGATE, UNIT_CGROUP_BOOL(u, delegate));
}
-int unit_fork_helper_process(Unit *u, pid_t *ret) {
- pid_t pid;
+int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret) {
int r;
assert(u);
@@ -4980,32 +5040,24 @@ int unit_fork_helper_process(Unit *u, pid_t *ret) {
(void) unit_realize_cgroup(u);
- pid = fork();
- if (pid < 0)
- return -errno;
-
- if (pid == 0) {
+ r = safe_fork(name, FORK_REOPEN_LOG, ret);
+ if (r != 0)
+ return r;
- (void) default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1);
- (void) ignore_signals(SIGPIPE, -1);
+ (void) default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1);
+ (void) ignore_signals(SIGPIPE, -1);
- log_close();
- log_open();
+ (void) prctl(PR_SET_PDEATHSIG, SIGTERM);
- if (u->cgroup_path) {
- r = cg_attach_everywhere(u->manager->cgroup_supported, u->cgroup_path, 0, NULL, NULL);
- if (r < 0) {
- log_unit_error_errno(u, r, "Failed to join unit cgroup %s: %m", u->cgroup_path);
- _exit(EXIT_CGROUP);
- }
+ if (u->cgroup_path) {
+ r = cg_attach_everywhere(u->manager->cgroup_supported, u->cgroup_path, 0, NULL, NULL);
+ if (r < 0) {
+ log_unit_error_errno(u, r, "Failed to join unit cgroup %s: %m", u->cgroup_path);
+ _exit(EXIT_CGROUP);
}
-
- *ret = getpid_cached();
- return 0;
}
- *ret = pid;
- return 1;
+ return 0;
}
static void unit_update_dependency_mask(Unit *u, UnitDependency d, Unit *other, UnitDependencyInfo di) {
@@ -5301,6 +5353,28 @@ void unit_warn_leftover_processes(Unit *u) {
(void) cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_leftover, u);
}
+bool unit_needs_console(Unit *u) {
+ ExecContext *ec;
+ UnitActiveState state;
+
+ assert(u);
+
+ state = unit_active_state(u);
+
+ if (UNIT_IS_INACTIVE_OR_FAILED(state))
+ return false;
+
+ if (UNIT_VTABLE(u)->needs_console)
+ return UNIT_VTABLE(u)->needs_console(u);
+
+ /* If this unit type doesn't implement this call, let's use a generic fallback implementation: */
+ ec = unit_get_exec_context(u);
+ if (!ec)
+ return false;
+
+ return exec_context_may_touch_console(ec);
+}
+
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
[COLLECT_INACTIVE] = "inactive",
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
diff --git a/src/core/unit.h b/src/core/unit.h
index fdd82315ba..8c79d4ed2e 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -236,8 +236,10 @@ struct Unit {
* process SIGCHLD for */
Set *pids;
- /* Used in sigchld event invocation to avoid repeat events being invoked */
- uint64_t sigchldgen;
+ /* Used in SIGCHLD and sd_notify() message event invocation logic to avoid that we dispatch the same event
+ * multiple times on the same unit. */
+ unsigned sigchldgen;
+ unsigned notifygen;
/* Used during GC sweeps */
unsigned gc_marker;
@@ -338,6 +340,7 @@ struct Unit {
bool sent_dbus_new_signal:1;
bool in_audit:1;
+ bool on_console:1;
bool cgroup_realized:1;
bool cgroup_members_mask_valid:1;
@@ -506,7 +509,7 @@ struct UnitVTable {
void (*notify_cgroup_empty)(Unit *u);
/* Called whenever a process of this unit sends us a message */
- void (*notify_message)(Unit *u, pid_t pid, char **tags, FDSet *fds);
+ void (*notify_message)(Unit *u, const struct ucred *ucred, char **tags, FDSet *fds);
/* Called whenever a name this Unit registered for comes or goes away. */
void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner);
@@ -539,6 +542,9 @@ struct UnitVTable {
/* Returns the main PID if there is any defined, or 0. */
pid_t (*control_pid)(Unit *u);
+ /* Returns true if the unit currently needs access to the console */
+ bool (*needs_console)(Unit *u);
+
/* This is called for each unit type and should be used to
* enumerate existing devices and load them. However,
* everything that is loaded here should still stay in
@@ -760,7 +766,7 @@ static inline bool unit_supported(Unit *u) {
}
void unit_warn_if_dir_nonempty(Unit *u, const char* where);
-int unit_fail_if_symlink(Unit *u, const char* where);
+int unit_fail_if_noncanonical(Unit *u, const char* where);
int unit_start_limit_test(Unit *u);
@@ -782,7 +788,7 @@ bool unit_shall_confirm_spawn(Unit *u);
void unit_set_exec_params(Unit *s, ExecParameters *p);
-int unit_fork_helper_process(Unit *u, pid_t *ret);
+int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret);
void unit_remove_dependencies(Unit *u, UnitDependencyMask mask);
@@ -793,6 +799,8 @@ int unit_prepare_exec(Unit *u);
void unit_warn_leftover_processes(Unit *u);
+bool unit_needs_console(Unit *u);
+
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full(unit, level, error, ...) \
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index e6063cc980..fdcea22f56 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -1126,7 +1126,7 @@ static int gather_pid_metadata(
/* If this is PID 1 disable coredump collection, we'll unlikely be able to process it later on. */
if (is_pid1_crash((const char**) context)) {
log_notice("Due to PID 1 having crashed coredump collection will now be turned off.");
- (void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
+ disable_coredumps();
}
set_iovec_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]);
diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c
index 0d420b2190..96e4a3e7e2 100644
--- a/src/coredump/coredumpctl.c
+++ b/src/coredump/coredumpctl.c
@@ -885,7 +885,6 @@ static int run_gdb(sd_journal *j) {
_cleanup_free_ char *exe = NULL, *path = NULL;
bool unlink_path = false;
const char *data;
- siginfo_t st;
size_t len;
pid_t pid;
int r;
@@ -928,28 +927,16 @@ static int run_gdb(sd_journal *j) {
/* Don't interfere with gdb and its handling of SIGINT. */
(void) ignore_signals(SIGINT, -1);
- pid = fork();
- if (pid < 0) {
- r = log_error_errno(errno, "Failed to fork(): %m");
+ r = safe_fork("(gdb)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ if (r < 0)
goto finish;
- }
- if (pid == 0) {
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
+ if (r == 0) {
execlp("gdb", "gdb", exe, path, NULL);
-
log_error_errno(errno, "Failed to invoke gdb: %m");
- _exit(1);
- }
-
- r = wait_for_terminate(pid, &st);
- if (r < 0) {
- log_error_errno(r, "Failed to wait for gdb: %m");
- goto finish;
+ _exit(EXIT_FAILURE);
}
- r = st.si_code == CLD_EXITED ? st.si_status : 255;
+ r = wait_for_terminate_and_check("gdb", pid, WAIT_LOG_ABNORMAL);
finish:
(void) default_signals(SIGINT, -1);
@@ -1061,7 +1048,7 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto end;
- if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
+ if (DEBUG_LOGGING) {
_cleanup_free_ char *filter;
filter = journal_make_match_string(j);
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
index 7e61332e52..acde2a6a32 100644
--- a/src/cryptsetup/cryptsetup-generator.c
+++ b/src/cryptsetup/cryptsetup-generator.c
@@ -61,7 +61,7 @@ static int create_disk(
const char *password,
const char *options) {
- _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *e = NULL,
+ _cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
*filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *dmname;
@@ -90,18 +90,14 @@ static int create_disk(
if (!e)
return log_oom();
- r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
- if (r < 0)
- return log_error_errno(r, "Failed to generate unit name: %m");
-
- p = strjoin(arg_dest, "/", n);
- if (!p)
- return log_oom();
-
u = fstab_node_to_udev_node(device);
if (!u)
return log_oom();
+ r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
+
u_escaped = specifier_escape(u);
if (!u_escaped)
return log_oom();
@@ -110,18 +106,17 @@ static int create_disk(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- password_escaped = specifier_escape(password);
- if (!password_escaped)
- return log_oom();
-
- f = fopen(p, "wxe");
- if (!f)
- return log_error_errno(errno, "Failed to create unit file %s: %m", p);
+ if (password) {
+ password_escaped = specifier_escape(password);
+ if (!password_escaped)
+ return log_oom();
+ }
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+ r = generator_open_unit_file(arg_dest, NULL, n, &f);
+ if (r < 0)
+ return r;
fprintf(f,
- "# Automatically generated by systemd-cryptsetup-generator\n\n"
"[Unit]\n"
"Description=Cryptography Setup for %%I\n"
"Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
@@ -183,9 +178,11 @@ static int create_disk(
if (r < 0)
return r;
- filtered_escaped = specifier_escape(filtered);
- if (!filtered_escaped)
- return log_oom();
+ if (filtered) {
+ filtered_escaped = specifier_escape(filtered);
+ if (!filtered_escaped)
+ return log_oom();
+ }
fprintf(f,
"\n[Service]\n"
@@ -210,7 +207,7 @@ static int create_disk(
r = fflush_and_check(f);
if (r < 0)
- return log_error_errno(r, "Failed to write file %s: %m", p);
+ return log_error_errno(r, "Failed to write unit file %s: %m", n);
if (!noauto) {
r = generator_add_symlink(arg_dest, d, "wants", n);
@@ -475,7 +472,8 @@ int main(int argc, char *argv[]) {
if (argc > 1)
arg_dest = argv[1];
- log_set_target(LOG_TARGET_SAFE);
+ log_set_prohibit_ipc(true);
+ log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 21c51022ef..7255ff418c 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -47,7 +47,7 @@ static char *arg_cipher = NULL;
static unsigned arg_key_size = 0;
static int arg_key_slot = CRYPT_ANY_SLOT;
static unsigned arg_keyfile_size = 0;
-static unsigned arg_keyfile_offset = 0;
+static uint64_t arg_keyfile_offset = 0;
static char *arg_hash = NULL;
static char *arg_header = NULL;
static unsigned arg_tries = 3;
@@ -131,13 +131,22 @@ static int parse_one_option(const char *option) {
}
} else if ((val = startswith(option, "keyfile-offset="))) {
+ uint64_t off;
- r = safe_atou(val, &arg_keyfile_offset);
+ r = safe_atou64(val, &off);
if (r < 0) {
log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
+ if ((size_t) off != off) {
+ /* https://gitlab.com/cryptsetup/cryptsetup/issues/359 */
+ log_error("keyfile-offset= value would truncated to %zu, ignoring.", (size_t) off);
+ return 0;
+ }
+
+ arg_keyfile_offset = off;
+
} else if ((val = startswith(option, "hash="))) {
r = free_and_strdup(&arg_hash, val);
if (r < 0)
diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c
index 604faa0d18..61c890d05a 100644
--- a/src/debug-generator/debug-generator.c
+++ b/src/debug-generator/debug-generator.c
@@ -165,7 +165,8 @@ int main(int argc, char *argv[]) {
if (argc > 1)
arg_dest = argv[2];
- log_set_target(LOG_TARGET_SAFE);
+ log_set_prohibit_ipc(true);
+ log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
diff --git a/src/delta/delta.c b/src/delta/delta.c
index d286881698..645b0b2278 100644
--- a/src/delta/delta.c
+++ b/src/delta/delta.c
@@ -161,8 +161,8 @@ static int notify_override_unchanged(const char *f) {
static int found_override(const char *top, const char *bottom) {
_cleanup_free_ char *dest = NULL;
- int k;
pid_t pid;
+ int r;
assert(top);
assert(bottom);
@@ -170,40 +170,35 @@ static int found_override(const char *top, const char *bottom) {
if (null_or_empty_path(top) > 0)
return notify_override_masked(top, bottom);
- k = readlink_malloc(top, &dest);
- if (k >= 0) {
+ r = readlink_malloc(top, &dest);
+ if (r >= 0) {
if (equivalent(dest, bottom) > 0)
return notify_override_equivalent(top, bottom);
else
return notify_override_redirected(top, bottom);
}
- k = notify_override_overridden(top, bottom);
+ r = notify_override_overridden(top, bottom);
if (!arg_diff)
- return k;
+ return r;
putchar('\n');
fflush(stdout);
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork off diff: %m");
- else if (pid == 0) {
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
+ r = safe_fork("(diff)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
execlp("diff", "diff", "-us", "--", bottom, top, NULL);
log_error_errno(errno, "Failed to execute diff: %m");
_exit(EXIT_FAILURE);
}
- wait_for_terminate_and_warn("diff", pid, false);
+ (void) wait_for_terminate_and_check("diff", pid, WAIT_LOG_ABNORMAL);
putchar('\n');
- return k;
+ return r;
}
static int enumerate_dir_d(
@@ -392,19 +387,28 @@ static int enumerate_dir(
return 0;
}
-static int should_skip_prefix(const char* p) {
+static bool should_skip_path(const char *prefix, const char *suffix) {
#if HAVE_SPLIT_USR
- int r;
_cleanup_free_ char *target = NULL;
+ const char *p;
+ char *dirname;
- r = chase_symlinks(p, NULL, 0, &target);
- if (r < 0)
- return r;
+ dirname = strjoina(prefix, "/", suffix);
- return !streq(p, target) && nulstr_contains(prefixes, target);
-#else
- return 0;
+ if (chase_symlinks(dirname, NULL, 0, &target) < 0)
+ return false;
+
+ NULSTR_FOREACH(p, prefixes) {
+ if (path_startswith(dirname, p))
+ continue;
+
+ if (path_equal(target, strjoina(p, "/", suffix))) {
+ log_debug("%s redirects to %s, skipping.", dirname, target);
+ return true;
+ }
+ }
#endif
+ return false;
}
static int process_suffix(const char *suffix, const char *onlyprefix) {
@@ -434,14 +438,8 @@ static int process_suffix(const char *suffix, const char *onlyprefix) {
NULSTR_FOREACH(p, prefixes) {
_cleanup_free_ char *t = NULL;
- int skip;
- skip = should_skip_prefix(p);
- if (skip < 0) {
- r = skip;
- goto finish;
- }
- if (skip)
+ if (should_skip_path(p, suffix))
continue;
t = strjoin(p, "/", suffix);
@@ -520,19 +518,12 @@ static int process_suffix_chop(const char *arg) {
/* Strip prefix from the suffix */
NULSTR_FOREACH(p, prefixes) {
const char *suffix;
- int skip;
-
- skip = should_skip_prefix(p);
- if (skip < 0)
- return skip;
- if (skip)
- continue;
suffix = startswith(arg, p);
if (suffix) {
suffix += strspn(suffix, "/");
if (*suffix)
- return process_suffix(suffix, NULL);
+ return process_suffix(suffix, p);
else
return process_suffixes(arg);
}
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index 207ddeb70f..262e520d56 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -23,6 +23,21 @@
#include <shadow.h>
#include <unistd.h>
+#ifdef HAVE_CRYPT_H
+/* libxcrypt is a replacement for glibc's libcrypt, and libcrypt might be
+ * removed from glibc at some point. As part of the removal, defines for
+ * crypt(3) are dropped from unistd.h, and we must include crypt.h instead.
+ *
+ * Newer versions of glibc (v2.0+) already ship crypt.h with a definition
+ * of crypt(3) as well, so we simply include it if it is present. MariaDB,
+ * MySQL, PostgreSQL, Perl and some other wide-spread packages do it the
+ * same way since ages without any problems.
+ */
+# include <crypt.h>
+#endif
+
+#include "sd-id128.h"
+
#include "alloc-util.h"
#include "ask-password-api.h"
#include "copy.h"
diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
index 0091e388dc..97d824aca4 100644
--- a/src/fsck/fsck.c
+++ b/src/fsck/fsck.c
@@ -284,9 +284,8 @@ int main(int argc, char *argv[]) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
const char *device, *type;
bool root_directory;
- siginfo_t status;
struct stat st;
- int r;
+ int r, exit_status;
pid_t pid;
if (argc > 2) {
@@ -392,12 +391,10 @@ int main(int argc, char *argv[]) {
}
}
- pid = fork();
- if (pid < 0) {
- r = log_error_errno(errno, "fork(): %m");
+ r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
goto finish;
- }
- if (pid == 0) {
+ if (r == 0) {
char dash_c[STRLEN("-C") + DECIMAL_STR_MAX(int) + 1];
int progress_socket = -1;
const char *cmdline[9];
@@ -405,10 +402,6 @@ int main(int argc, char *argv[]) {
/* Child */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
/* Close the reading side of the progress pipe */
progress_pipe[0] = safe_close(progress_pipe[0]);
@@ -455,38 +448,30 @@ int main(int argc, char *argv[]) {
(void) process_progress(progress_pipe[0]);
progress_pipe[0] = -1;
- r = wait_for_terminate(pid, &status);
- if (r < 0) {
- log_error_errno(r, "waitid(): %m");
+ exit_status = wait_for_terminate_and_check("fsck", pid, WAIT_LOG_ABNORMAL);
+ if (exit_status < 0) {
+ r = exit_status;
goto finish;
}
+ if (exit_status & ~1) {
+ log_error("fsck failed with exit status %i.", exit_status);
- if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
-
- if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED))
- log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
- else if (status.si_code == CLD_EXITED)
- log_error("fsck failed with error code %i.", status.si_status);
- else
- log_error("fsck failed due to unknown reason.");
-
- r = -EINVAL;
-
- if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory)
+ if ((exit_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory) {
/* System should be rebooted. */
start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly");
- else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED)))
+ r = -EINVAL;
+ } else if (exit_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED)) {
/* Some other problem */
start_target(SPECIAL_EMERGENCY_TARGET, "replace");
- else {
+ r = -EINVAL;
+ } else {
log_warning("Ignoring error.");
r = 0;
}
-
} else
r = 0;
- if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED))
+ if (exit_status & FSCK_ERROR_CORRECTED)
(void) touch("/run/systemd/quotacheck");
finish:
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 22c4ae9861..f392f89099 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -102,7 +102,7 @@ static int add_swap(
struct mntent *me,
MountpointFlags flags) {
- _cleanup_free_ char *name = NULL, *unit = NULL;
+ _cleanup_free_ char *name = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
@@ -123,19 +123,9 @@ static int add_swap(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- unit = strjoin(arg_dest, "/", name);
- if (!unit)
- return log_oom();
-
- f = fopen(unit, "wxe");
- if (!f)
- return log_error_errno(errno,
- errno == EEXIST ?
- "Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
- "Failed to create unit file %s: %m",
- unit);
-
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+ r = generator_open_unit_file(arg_dest, "/etc/fstab", name, &f);
+ if (r < 0)
+ return r;
fputs("# Automatically generated by systemd-fstab-generator\n\n"
"[Unit]\n"
@@ -153,7 +143,7 @@ static int add_swap(
r = fflush_and_check(f);
if (r < 0)
- return log_error_errno(r, "Failed to write unit file %s: %m", unit);
+ return log_error_errno(r, "Failed to write unit file %s: %m", name);
/* use what as where, to have a nicer error message */
r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL);
@@ -323,10 +313,9 @@ static int add_mount(
_cleanup_free_ char
*name = NULL,
- *automount_name = NULL, *automount_unit = NULL,
+ *automount_name = NULL,
*filtered = NULL,
*where_escaped = NULL;
- const char *unit;
_cleanup_fclose_ FILE *f = NULL;
int r;
@@ -363,20 +352,11 @@ static int add_mount(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- unit = strjoina(dest, "/", name);
-
- f = fopen(unit, "wxe");
- if (!f)
- return log_error_errno(errno,
- errno == EEXIST ?
- "Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
- "Failed to create unit file %s: %m",
- unit);
-
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+ r = generator_open_unit_file(dest, "/etc/fstab", name, &f);
+ if (r < 0)
+ return r;
fprintf(f,
- "# Automatically generated by systemd-fstab-generator\n\n"
"[Unit]\n"
"SourcePath=%s\n"
"Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
@@ -461,7 +441,7 @@ static int add_mount(
r = fflush_and_check(f);
if (r < 0)
- return log_error_errno(r, "Failed to write unit file %s: %m", unit);
+ return log_error_errno(r, "Failed to write unit file %s: %m", name);
if (flags & MAKEFS) {
r = generator_hook_up_mkfs(dest, what, where, fstype);
@@ -487,19 +467,13 @@ static int add_mount(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- automount_unit = strjoin(dest, "/", automount_name);
- if (!automount_unit)
- return log_oom();
-
fclose(f);
- f = fopen(automount_unit, "wxe");
- if (!f)
- return log_error_errno(errno, "Failed to create unit file %s: %m", automount_unit);
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+ r = generator_open_unit_file(dest, "/etc/fstab", automount_name, &f);
+ if (r < 0)
+ return r;
fprintf(f,
- "# Automatically generated by systemd-fstab-generator\n\n"
"[Unit]\n"
"SourcePath=%s\n"
"Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
@@ -534,7 +508,7 @@ static int add_mount(
r = fflush_and_check(f);
if (r < 0)
- return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit);
+ return log_error_errno(r, "Failed to write unit file %s: %m", automount_name);
r = generator_add_symlink(dest, post,
(flags & NOFAIL) ? "wants" : "requires", automount_name);
@@ -917,7 +891,8 @@ int main(int argc, char *argv[]) {
if (argc > 3)
arg_dest_late = argv[3];
- log_set_target(LOG_TARGET_SAFE);
+ log_set_prohibit_ipc(true);
+ log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
diff --git a/src/fuzz/fuzz-dhcp-server.c b/src/fuzz/fuzz-dhcp-server.c
new file mode 100644
index 0000000000..ba903c7158
--- /dev/null
+++ b/src/fuzz/fuzz-dhcp-server.c
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/***
+ Copyright 2018 Jonathan Rudenberg
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "fuzz.h"
+
+#include "sd-dhcp-server.c"
+
+/* stub out network so that the server doesn't send */
+ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) {
+ return len;
+}
+
+ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
+ struct in_addr address = {.s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))};
+ static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
+ uint8_t *client_id;
+ DHCPLease *lease;
+ int pool_offset;
+
+ if (size < sizeof(DHCPMessage))
+ return 0;
+
+ assert_se(sd_dhcp_server_new(&server, 1) >= 0);
+ server->fd = open("/dev/null", O_RDWR|O_CLOEXEC|O_NOCTTY);
+ assert_se(server->fd >= 0);
+ assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0);
+
+ /* add a lease to the pool to expose additional code paths */
+ client_id = malloc(2);
+ assert_se(client_id);
+ client_id[0] = 2;
+ client_id[1] = 2;
+ lease = new0(DHCPLease, 1);
+ assert_se(lease);
+ lease->client_id.length = 2;
+ lease->client_id.data = client_id;
+ lease->address = htobe32(UINT32_C(10) << 24 | UINT32_C(2));
+ lease->gateway = htobe32(UINT32_C(10) << 24 | UINT32_C(1));
+ lease->expiration = UINT64_MAX;
+ memcpy(lease->chaddr, chaddr, 16);
+ pool_offset = get_pool_offset(server, lease->address);
+ server->bound_leases[pool_offset] = lease;
+ assert_se(hashmap_put(server->leases_by_client_id, &lease->client_id, lease) >= 0);
+
+ (void) dhcp_server_handle_message(server, (DHCPMessage*)data, size);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-dns-packet.c b/src/fuzz/fuzz-dns-packet.c
new file mode 100644
index 0000000000..0f25081b22
--- /dev/null
+++ b/src/fuzz/fuzz-dns-packet.c
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/***
+ Copyright 2018 Jonathan Rudenberg
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "fuzz.h"
+#include "resolved-dns-packet.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+
+ if (size > DNS_PACKET_SIZE_MAX)
+ return 0;
+
+ assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX) >= 0);
+ p->size = 0; /* by default append starts after the header, undo that */
+ assert_se(dns_packet_append_blob(p, data, size, NULL) >= 0);
+ if (size < DNS_PACKET_HEADER_SIZE) {
+ /* make sure we pad the packet back up to the minimum header size */
+ assert_se(p->allocated >= DNS_PACKET_HEADER_SIZE);
+ memzero(DNS_PACKET_DATA(p) + size, DNS_PACKET_HEADER_SIZE - size);
+ p->size = DNS_PACKET_HEADER_SIZE;
+ }
+ (void) dns_packet_extract(p);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-dns-packet.options b/src/fuzz/fuzz-dns-packet.options
new file mode 100644
index 0000000000..0824b19fab
--- /dev/null
+++ b/src/fuzz/fuzz-dns-packet.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 65535
diff --git a/src/fuzz/fuzz-dns-server.options b/src/fuzz/fuzz-dns-server.options
new file mode 100644
index 0000000000..5c330e5cec
--- /dev/null
+++ b/src/fuzz/fuzz-dns-server.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 600
diff --git a/src/fuzz/fuzz-main.c b/src/fuzz/fuzz-main.c
new file mode 100644
index 0000000000..45e46907e2
--- /dev/null
+++ b/src/fuzz/fuzz-main.c
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/***
+ Copyright 2018 Jonathan Rudenberg
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "log.h"
+#include "fileio.h"
+#include "fuzz.h"
+
+/* This is a test driver for the systemd fuzzers that provides main function
+ * for regression testing outside of oss-fuzz (https://github.com/google/oss-fuzz)
+ *
+ * It reads files named on the command line and passes them one by one into the
+ * fuzzer that it is compiled into. */
+
+int main(int argc, char **argv) {
+ int i, r;
+ size_t size;
+ char *name;
+
+ log_set_max_level(LOG_DEBUG);
+ for (i = 1; i < argc; i++) {
+ _cleanup_free_ char *buf = NULL;
+
+ name = argv[i];
+ r = read_full_file(name, &buf, &size);
+ if (r < 0) {
+ log_error_errno(r, "Failed to open '%s': %m", name);
+ return EXIT_FAILURE;
+ }
+ printf("%s... ", name);
+ fflush(stdout);
+ (void) LLVMFuzzerTestOneInput((uint8_t*)buf, size);
+ printf("ok\n");
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/src/fuzz/fuzz.h b/src/fuzz/fuzz.h
new file mode 100644
index 0000000000..5293c5762f
--- /dev/null
+++ b/src/fuzz/fuzz.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/***
+ Copyright 2018 Jonathan Rudenberg
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* The entry point into the fuzzer */
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build
new file mode 100644
index 0000000000..09a8c8a11d
--- /dev/null
+++ b/src/fuzz/meson.build
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: LGPL-2.1+
+# Copyright 2018 Jonathan Rudenberg
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# systemd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with systemd; If not, see <http://www.gnu.org/licenses/>.
+
+fuzzers += [
+ [['src/fuzz/fuzz-dns-packet.c',
+ dns_type_headers],
+ [libsystemd_resolve_core,
+ libshared],
+ [libgcrypt,
+ libgpg_error,
+ libm]],
+ [['src/fuzz/fuzz-dhcp-server.c',
+ ],
+ [libsystemd_network,
+ libshared],
+ []]
+]
diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c
index 3f62a81abc..f475aef66c 100644
--- a/src/getty-generator/getty-generator.c
+++ b/src/getty-generator/getty-generator.c
@@ -138,7 +138,8 @@ int main(int argc, char *argv[]) {
if (argc > 1)
arg_dest = argv[1];
- log_set_target(LOG_TARGET_SAFE);
+ log_set_prohibit_ipc(true);
+ log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 9e8b956d5c..cbdbc5afe2 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -28,6 +28,7 @@
#include "alloc-util.h"
#include "blkid-util.h"
+#include "blockdev-util.h"
#include "btrfs-util.h"
#include "dirent-util.h"
#include "dissect-image.h"
@@ -712,7 +713,8 @@ int main(int argc, char *argv[]) {
if (argc > 1)
arg_dest = argv[3];
- log_set_target(LOG_TARGET_SAFE);
+ log_set_prohibit_ipc(true);
+ log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c
index 01222db516..a81386ef63 100644
--- a/src/hibernate-resume/hibernate-resume-generator.c
+++ b/src/hibernate-resume/hibernate-resume-generator.c
@@ -86,7 +86,8 @@ int main(int argc, char *argv[]) {
if (argc > 1)
arg_dest = argv[1];
- log_set_target(LOG_TARGET_SAFE);
+ log_set_prohibit_ipc(true);
+ log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index 5feaa60c99..1c8c76934c 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -680,9 +680,9 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
if (r < 0)
return log_error_errno(r, "Failed to register object: %m");
- r = sd_bus_request_name(bus, "org.freedesktop.hostname1", 0);
+ r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to register name: %m");
+ return log_error_errno(r, "Failed to request name: %m");
r = sd_bus_attach_event(bus, event, 0);
if (r < 0)
diff --git a/src/import/curl-util.c b/src/import/curl-util.c
index 7069c95a9f..62bbaa500d 100644
--- a/src/import/curl-util.c
+++ b/src/import/curl-util.c
@@ -21,6 +21,7 @@
#include "alloc-util.h"
#include "curl-util.h"
#include "fd-util.h"
+#include "locale-util.h"
#include "string-util.h"
static void curl_glue_check_finished(CurlGlue *g) {
@@ -414,8 +415,8 @@ int curl_header_strdup(const void *contents, size_t sz, const char *field, char
}
int curl_parse_http_time(const char *t, usec_t *ret) {
+ _cleanup_(freelocalep) locale_t loc = (locale_t) 0;
const char *e;
- locale_t loc;
struct tm tm;
time_t v;
@@ -434,7 +435,6 @@ int curl_parse_http_time(const char *t, usec_t *ret) {
if (!e || *e != 0)
/* ANSI C */
e = strptime_l(t, "%a %b %d %H:%M:%S %Y", &tm, loc);
- freelocale(loc);
if (!e || *e != 0)
return -EINVAL;
diff --git a/src/import/export.c b/src/import/export.c
index 753d1399f5..0f32b90051 100644
--- a/src/import/export.c
+++ b/src/import/export.c
@@ -21,6 +21,7 @@
#include <getopt.h>
#include "sd-event.h"
+#include "sd-id128.h"
#include "alloc-util.h"
#include "export-raw.h"
diff --git a/src/import/import-common.c b/src/import/import-common.c
index 2f989a171c..c24a0b0c86 100644
--- a/src/import/import-common.c
+++ b/src/import/import-common.c
@@ -27,6 +27,7 @@
#include "capability-util.h"
#include "fd-util.h"
#include "import-common.h"
+#include "process-util.h"
#include "signal-util.h"
#include "util.h"
@@ -82,11 +83,10 @@ int import_fork_tar_x(const char *path, pid_t *ret) {
if (pipe2(pipefd, O_CLOEXEC) < 0)
return log_error_errno(errno, "Failed to create pipe for tar: %m");
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork off tar: %m");
-
- if (pid == 0) {
+ r = safe_fork("(tar)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
int null_fd;
uint64_t retain =
(1ULL << CAP_CHOWN) |
@@ -98,10 +98,6 @@ int import_fork_tar_x(const char *path, pid_t *ret) {
/* Child */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
pipefd[1] = safe_close(pipefd[1]);
r = move_fd(pipefd[0], STDIN_FILENO, false);
@@ -156,20 +152,15 @@ int import_fork_tar_c(const char *path, pid_t *ret) {
if (pipe2(pipefd, O_CLOEXEC) < 0)
return log_error_errno(errno, "Failed to create pipe for tar: %m");
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork off tar: %m");
-
- if (pid == 0) {
+ r = safe_fork("(tar)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
int null_fd;
uint64_t retain = (1ULL << CAP_DAC_OVERRIDE);
/* Child */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
pipefd[0] = safe_close(pipefd[0]);
r = move_fd(pipefd[1], STDOUT_FILENO, false);
diff --git a/src/import/import-compress.c b/src/import/import-compress.c
index cb5b9821c3..acb47fef14 100644
--- a/src/import/import-compress.c
+++ b/src/import/import-compress.c
@@ -70,7 +70,7 @@ int import_uncompress_detect(ImportCompress *c, const void *data, size_t size) {
if (memcmp(data, xz_signature, sizeof(xz_signature)) == 0) {
lzma_ret xzr;
- xzr = lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK);
+ xzr = lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED);
if (xzr != LZMA_OK)
return -EIO;
diff --git a/src/import/import-tar.c b/src/import/import-tar.c
index c5014499ac..09c7654adc 100644
--- a/src/import/import-tar.c
+++ b/src/import/import-tar.c
@@ -190,7 +190,7 @@ static int tar_import_finish(TarImport *i) {
i->tar_fd = safe_close(i->tar_fd);
if (i->tar_pid > 0) {
- r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
+ r = wait_for_terminate_and_check("tar", i->tar_pid, WAIT_LOG);
i->tar_pid = 0;
if (r < 0)
return r;
diff --git a/src/import/import.c b/src/import/import.c
index cc454ee15b..454e64e3cb 100644
--- a/src/import/import.c
+++ b/src/import/import.c
@@ -21,6 +21,7 @@
#include <getopt.h>
#include "sd-event.h"
+#include "sd-id128.h"
#include "alloc-util.h"
#include "fd-util.h"
diff --git a/src/import/importd.c b/src/import/importd.c
index 9c7694c0ad..98ee1a2fab 100644
--- a/src/import/importd.c
+++ b/src/import/importd.c
@@ -371,10 +371,10 @@ static int transfer_start(Transfer *t) {
if (pipe2(pipefd, O_CLOEXEC) < 0)
return -errno;
- t->pid = fork();
- if (t->pid < 0)
- return -errno;
- if (t->pid == 0) {
+ r = safe_fork("(sd-transfer)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &t->pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
const char *cmd[] = {
NULL, /* systemd-import, systemd-export or systemd-pull */
NULL, /* tar, raw */
@@ -393,10 +393,6 @@ static int transfer_start(Transfer *t) {
/* Child */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
pipefd[0] = safe_close(pipefd[0]);
if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
@@ -1153,9 +1149,9 @@ static int manager_add_bus_objects(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to add transfer enumerator: %m");
- r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
+ r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.import1", 0, NULL, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to register name: %m");
+ return log_error_errno(r, "Failed to request name: %m");
r = sd_bus_attach_event(m->bus, m->event, 0);
if (r < 0)
diff --git a/src/import/pull-common.c b/src/import/pull-common.c
index c2a3a6aa8b..ecdcbd2dc2 100644
--- a/src/import/pull-common.c
+++ b/src/import/pull-common.c
@@ -463,10 +463,10 @@ int pull_verify(PullJob *main_job,
gpg_home_created = true;
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork off gpg: %m");
- if (pid == 0) {
+ r = safe_fork("(gpg)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
const char *cmd[] = {
"gpg",
"--no-options",
@@ -487,10 +487,6 @@ int pull_verify(PullJob *main_job,
/* Child */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
gpg_pipe[1] = safe_close(gpg_pipe[1]);
r = move_fd(gpg_pipe[0], STDIN_FILENO, false);
@@ -546,11 +542,11 @@ int pull_verify(PullJob *main_job,
gpg_pipe[1] = safe_close(gpg_pipe[1]);
- r = wait_for_terminate_and_warn("gpg", pid, true);
+ r = wait_for_terminate_and_check("gpg", pid, WAIT_LOG_ABNORMAL);
pid = 0;
if (r < 0)
goto finish;
- if (r > 0) {
+ if (r != EXIT_SUCCESS) {
log_error("DOWNLOAD INVALID: Signature verification failed.");
r = -EBADMSG;
} else {
diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c
index ed91511245..6ee63bdad5 100644
--- a/src/import/pull-tar.c
+++ b/src/import/pull-tar.c
@@ -333,11 +333,11 @@ static void tar_pull_job_on_finished(PullJob *j) {
goto finish;
if (i->tar_pid > 0) {
- r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
+ r = wait_for_terminate_and_check("tar", i->tar_pid, WAIT_LOG);
i->tar_pid = 0;
if (r < 0)
goto finish;
- if (r > 0) {
+ if (r != EXIT_SUCCESS) {
r = -EIO;
goto finish;
}
diff --git a/src/import/pull.c b/src/import/pull.c
index 46e0fd5acb..325f7e3d50 100644
--- a/src/import/pull.c
+++ b/src/import/pull.c
@@ -21,6 +21,7 @@
#include <getopt.h>
#include "sd-event.h"
+#include "sd-id128.h"
#include "alloc-util.h"
#include "hostname-util.h"
diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c
index 5488999727..c1af13d15b 100644
--- a/src/initctl/initctl.c
+++ b/src/initctl/initctl.c
@@ -38,6 +38,7 @@
#include "log.h"
#include "special.h"
#include "util.h"
+#include "process-util.h"
#define SERVER_FD_MAX 16
#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 82c70cfbe3..5a437ce031 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -226,7 +226,8 @@ static ssize_t request_reader_entries(
return MHD_CONTENT_READER_END_WITH_ERROR;
}
- r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL, NULL);
+ r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH,
+ NULL, NULL, NULL);
if (r < 0) {
log_error_errno(r, "Failed to serialize item: %m");
return MHD_CONTENT_READER_END_WITH_ERROR;
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index e44989e1ba..66d5369a54 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -43,6 +43,7 @@
#include "journald-native.h"
#include "macro.h"
#include "parse-util.h"
+#include "process-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "stat-util.h"
@@ -81,27 +82,20 @@ static bool arg_trust_all = false;
**********************************************************************/
static int spawn_child(const char* child, char** argv) {
- int fd[2];
- pid_t parent_pid, child_pid;
- int r;
+ pid_t child_pid;
+ int fd[2], r;
if (pipe(fd) < 0)
return log_error_errno(errno, "Failed to create pager pipe: %m");
- parent_pid = getpid_cached();
-
- child_pid = fork();
- if (child_pid < 0) {
- r = log_error_errno(errno, "Failed to fork: %m");
+ r = safe_fork("(remote)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid);
+ if (r < 0) {
safe_close_pair(fd);
return r;
}
/* In the child */
- if (child_pid == 0) {
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
+ if (r == 0) {
r = dup2(fd[1], STDOUT_FILENO);
if (r < 0) {
@@ -111,15 +105,6 @@ static int spawn_child(const char* child, char** argv) {
safe_close_pair(fd);
- /* Make sure the child goes away when the parent dies */
- if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
- _exit(EXIT_FAILURE);
-
- /* Check whether our parent died before we were able
- * to set the death signal */
- if (getppid() != parent_pid)
- _exit(EXIT_SUCCESS);
-
execvp(child, argv);
log_error_errno(errno, "Failed to exec child %s: %m", child);
_exit(EXIT_FAILURE);
diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c
index 69718aae87..0b74ca98a7 100644
--- a/src/journal-remote/journal-upload.c
+++ b/src/journal-remote/journal-upload.c
@@ -37,6 +37,7 @@
#include "log.h"
#include "mkdir.h"
#include "parse-util.h"
+#include "process-util.h"
#include "sigbus.h"
#include "signal-util.h"
#include "string-util.h"
@@ -248,7 +249,7 @@ int start_upload(Uploader *u,
easy_setopt(curl, CURLOPT_HTTPHEADER, u->header,
LOG_ERR, return -EXFULL);
- if (_unlikely_(log_get_max_level() >= LOG_DEBUG))
+ if (DEBUG_LOGGING)
/* enable verbose for easier tracing */
easy_setopt(curl, CURLOPT_VERBOSE, 1L, LOG_WARNING, );
diff --git a/src/journal/generate-audit_type-list.sh b/src/journal/generate-audit_type-list.sh
index 18cbe0599c..2445a02668 100755
--- a/src/journal/generate-audit_type-list.sh
+++ b/src/journal/generate-audit_type-list.sh
@@ -1,4 +1,5 @@
-#!/bin/sh -eu
+#!/bin/sh
+set -eu
cpp="$1"
shift
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 7fef403391..3353b3a0d8 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -147,6 +147,8 @@ static void journal_file_set_offline_internal(JournalFile *f) {
static void * journal_file_set_offline_thread(void *arg) {
JournalFile *f = arg;
+ (void) pthread_setname_np(pthread_self(), "journal-offline");
+
journal_file_set_offline_internal(f);
return NULL;
@@ -245,11 +247,25 @@ int journal_file_set_offline(JournalFile *f, bool wait) {
if (wait) /* Without using a thread if waiting. */
journal_file_set_offline_internal(f);
else {
+ sigset_t ss, saved_ss;
+ int k;
+
+ if (sigfillset(&ss) < 0)
+ return -errno;
+
+ r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss);
+ if (r > 0)
+ return -r;
+
r = pthread_create(&f->offline_thread, NULL, journal_file_set_offline_thread, f);
+
+ k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL);
if (r > 0) {
f->offline_state = OFFLINE_JOINED;
return -r;
}
+ if (k > 0)
+ return -k;
}
return 0;
@@ -3237,11 +3253,8 @@ int journal_file_open(
if (!IN_SET((flags & O_ACCMODE), O_RDONLY, O_RDWR))
return -EINVAL;
- if (fname) {
- if (!endswith(fname, ".journal") &&
- !endswith(fname, ".journal~"))
- return -EINVAL;
- }
+ if (fname && (flags & O_CREAT) && !endswith(fname, ".journal"))
+ return -EINVAL;
f = new0(JournalFile, 1);
if (!f)
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index a78aa07032..73329ba024 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -416,10 +416,9 @@ _public_ int sd_journal_stream_fd(const char *identifier, int priority, int leve
if (shutdown(fd, SHUT_RD) < 0)
return -errno;
- fd_inc_sndbuf(fd, SNDBUF_SIZE);
+ (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
- if (!identifier)
- identifier = "";
+ identifier = strempty(identifier);
l = strlen(identifier);
header = alloca(l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 956d85aff2..17782688d9 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -34,6 +34,11 @@
#include <sys/stat.h>
#include <unistd.h>
+#if HAVE_PCRE2
+# define PCRE2_CODE_UNIT_WIDTH 8
+# include <pcre2.h>
+#endif
+
#include "sd-bus.h"
#include "sd-journal.h"
@@ -76,6 +81,34 @@
#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
+#if HAVE_PCRE2
+DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free);
+
+static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
+ int errorcode, r;
+ PCRE2_SIZE erroroffset;
+ pcre2_code *p;
+
+ p = pcre2_compile((PCRE2_SPTR8) pattern,
+ PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
+ if (!p) {
+ unsigned char buf[LINE_MAX];
+
+ r = pcre2_get_error_message(errorcode, buf, sizeof buf);
+
+ log_error("Bad pattern \"%s\": %s",
+ pattern,
+ r < 0 ? "unknown error" : (char*) buf);
+ return -EINVAL;
+ }
+
+ *out = p;
+ return 0;
+}
+
+#endif
+
enum {
/* Special values for arg_lines */
ARG_LINES_DEFAULT = -2,
@@ -126,6 +159,12 @@ static uint64_t arg_vacuum_n_files = 0;
static usec_t arg_vacuum_time = 0;
static char **arg_output_fields = NULL;
+#if HAVE_PCRE2
+static const char *arg_pattern = NULL;
+static pcre2_code *arg_compiled_pattern = NULL;
+static int arg_case_sensitive = -1; /* -1 means be smart */
+#endif
+
static enum {
ACTION_SHOW,
ACTION_NEW_ID128,
@@ -280,67 +319,69 @@ static void help(void) {
printf("%s [OPTIONS...] [MATCHES...]\n\n"
"Query the journal.\n\n"
"Options:\n"
- " --system Show the system journal\n"
- " --user Show the user journal for the current user\n"
- " -M --machine=CONTAINER Operate on local container\n"
- " -S --since=DATE Show entries not older than the specified date\n"
- " -U --until=DATE Show entries not newer than the specified date\n"
- " -c --cursor=CURSOR Show entries starting at the specified cursor\n"
- " --after-cursor=CURSOR Show entries after the specified cursor\n"
- " --show-cursor Print the cursor after all the entries\n"
- " -b --boot[=ID] Show current boot or the specified boot\n"
- " --list-boots Show terse information about recorded boots\n"
- " -k --dmesg Show kernel message log from the current boot\n"
- " -u --unit=UNIT Show logs from the specified unit\n"
- " --user-unit=UNIT Show logs from the specified user unit\n"
- " -t --identifier=STRING Show entries with the specified syslog identifier\n"
- " -p --priority=RANGE Show entries with the specified priority\n"
- " -e --pager-end Immediately jump to the end in the pager\n"
- " -f --follow Follow the journal\n"
- " -n --lines[=INTEGER] Number of journal entries to show\n"
- " --no-tail Show all lines, even in follow mode\n"
- " -r --reverse Show the newest entries first\n"
- " -o --output=STRING Change journal output mode (short, short-precise,\n"
- " short-iso, short-iso-precise, short-full,\n"
- " short-monotonic, short-unix, verbose, export,\n"
- " json, json-pretty, json-sse, cat)\n"
- " --output-fields=LIST Select fields to print in verbose/export/json modes\n"
- " --utc Express time in Coordinated Universal Time (UTC)\n"
- " -x --catalog Add message explanations where available\n"
- " --no-full Ellipsize fields\n"
- " -a --all Show all fields, including long and unprintable\n"
- " -q --quiet Do not show info messages and privilege warning\n"
- " --no-pager Do not pipe output into a pager\n"
- " --no-hostname Suppress output of hostname field\n"
- " -m --merge Show entries from all available journals\n"
- " -D --directory=PATH Show journal files from directory\n"
- " --file=PATH Show journal file\n"
- " --root=ROOT Operate on files below a root directory\n"
+ " --system Show the system journal\n"
+ " --user Show the user journal for the current user\n"
+ " -M --machine=CONTAINER Operate on local container\n"
+ " -S --since=DATE Show entries not older than the specified date\n"
+ " -U --until=DATE Show entries not newer than the specified date\n"
+ " -c --cursor=CURSOR Show entries starting at the specified cursor\n"
+ " --after-cursor=CURSOR Show entries after the specified cursor\n"
+ " --show-cursor Print the cursor after all the entries\n"
+ " -b --boot[=ID] Show current boot or the specified boot\n"
+ " --list-boots Show terse information about recorded boots\n"
+ " -k --dmesg Show kernel message log from the current boot\n"
+ " -u --unit=UNIT Show logs from the specified unit\n"
+ " --user-unit=UNIT Show logs from the specified user unit\n"
+ " -t --identifier=STRING Show entries with the specified syslog identifier\n"
+ " -p --priority=RANGE Show entries with the specified priority\n"
+ " -g --grep=PATTERN Show entries with MESSSAGE matching PATTERN\n"
+ " --case-sensitive[=BOOL] Force case sensitive or insenstive matching\n"
+ " -e --pager-end Immediately jump to the end in the pager\n"
+ " -f --follow Follow the journal\n"
+ " -n --lines[=INTEGER] Number of journal entries to show\n"
+ " --no-tail Show all lines, even in follow mode\n"
+ " -r --reverse Show the newest entries first\n"
+ " -o --output=STRING Change journal output mode (short, short-precise,\n"
+ " short-iso, short-iso-precise, short-full,\n"
+ " short-monotonic, short-unix, verbose, export,\n"
+ " json, json-pretty, json-sse, cat)\n"
+ " --output-fields=LIST Select fields to print in verbose/export/json modes\n"
+ " --utc Express time in Coordinated Universal Time (UTC)\n"
+ " -x --catalog Add message explanations where available\n"
+ " --no-full Ellipsize fields\n"
+ " -a --all Show all fields, including long and unprintable\n"
+ " -q --quiet Do not show info messages and privilege warning\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --no-hostname Suppress output of hostname field\n"
+ " -m --merge Show entries from all available journals\n"
+ " -D --directory=PATH Show journal files from directory\n"
+ " --file=PATH Show journal file\n"
+ " --root=ROOT Operate on files below a root directory\n"
#if HAVE_GCRYPT
- " --interval=TIME Time interval for changing the FSS sealing key\n"
- " --verify-key=KEY Specify FSS verification key\n"
- " --force Override of the FSS key pair with --setup-keys\n"
+ " --interval=TIME Time interval for changing the FSS sealing key\n"
+ " --verify-key=KEY Specify FSS verification key\n"
+ " --force Override of the FSS key pair with --setup-keys\n"
#endif
"\nCommands:\n"
- " -h --help Show this help text\n"
- " --version Show package version\n"
- " -N --fields List all field names currently used\n"
- " -F --field=FIELD List all values that a specified field takes\n"
- " --disk-usage Show total disk usage of all journal files\n"
- " --vacuum-size=BYTES Reduce disk usage below specified size\n"
- " --vacuum-files=INT Leave only the specified number of journal files\n"
- " --vacuum-time=TIME Remove journal files older than specified time\n"
- " --verify Verify journal file consistency\n"
- " --sync Synchronize unwritten journal messages to disk\n"
- " --flush Flush all journal data from /run into /var\n"
- " --rotate Request immediate rotation of the journal files\n"
- " --header Show journal header information\n"
- " --list-catalog Show all message IDs in the catalog\n"
- " --dump-catalog Show entries in the message catalog\n"
- " --update-catalog Update the message catalog database\n"
- " --new-id128 Generate a new 128-bit ID\n"
+ " -h --help Show this help text\n"
+ " --version Show package version\n"
+ " -N --fields List all field names currently used\n"
+ " -F --field=FIELD List all values that a specified field takes\n"
+ " --disk-usage Show total disk usage of all journal files\n"
+ " --vacuum-size=BYTES Reduce disk usage below specified size\n"
+ " --vacuum-files=INT Leave only the specified number of journal files\n"
+ " --vacuum-time=TIME Remove journal files older than specified time\n"
+ " --verify Verify journal file consistency\n"
+ " --sync Synchronize unwritten journal messages to disk\n"
+ " --flush Flush all journal data from /run into /var\n"
+ " --rotate Request immediate rotation of the journal files\n"
+ " --header Show journal header information\n"
+ " --list-catalog Show all message IDs in the catalog\n"
+ " --dump-catalog Show entries in the message catalog\n"
+ " --update-catalog Update the message catalog database\n"
+ " --new-id128 Generate a new 128-bit ID\n"
#if HAVE_GCRYPT
- " --setup-keys Generate a new FSS key pair\n"
+ " --setup-keys Generate a new FSS key pair\n"
#endif
, program_invocation_short_name);
}
@@ -372,6 +413,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_DUMP_CATALOG,
ARG_UPDATE_CATALOG,
ARG_FORCE,
+ ARG_CASE_SENSITIVE,
ARG_UTC,
ARG_SYNC,
ARG_FLUSH,
@@ -411,6 +453,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "header", no_argument, NULL, ARG_HEADER },
{ "identifier", required_argument, NULL, 't' },
{ "priority", required_argument, NULL, 'p' },
+ { "grep", required_argument, NULL, 'g' },
+ { "case-sensitive", optional_argument, NULL, ARG_CASE_SENSITIVE },
{ "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
{ "interval", required_argument, NULL, ARG_INTERVAL },
{ "verify", no_argument, NULL, ARG_VERIFY },
@@ -762,6 +806,27 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
+#if HAVE_PCRE2
+ case 'g':
+ arg_pattern = optarg;
+ break;
+
+ case ARG_CASE_SENSITIVE:
+ if (optarg) {
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Bad --case-sensitive= argument \"%s\": %m", optarg);
+ arg_case_sensitive = r;
+ } else
+ arg_case_sensitive = true;
+
+ break;
+#else
+ case 'g':
+ case ARG_CASE_SENSITIVE:
+ return log_error("Compiled without pattern matching support");
+#endif
+
case 'S':
r = parse_timestamp(optarg, &arg_since);
if (r < 0) {
@@ -917,6 +982,42 @@ static int parse_argv(int argc, char *argv[]) {
arg_system_units = strv_free(arg_system_units);
}
+
+#if HAVE_PCRE2
+ if (arg_pattern) {
+ unsigned flags;
+
+ if (arg_case_sensitive >= 0)
+ flags = !arg_case_sensitive * PCRE2_CASELESS;
+ else {
+ _cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
+ bool has_case;
+ _cleanup_(pcre2_code_freep) pcre2_code *cs = NULL;
+
+ md = pcre2_match_data_create(1, NULL);
+ if (!md)
+ return log_oom();
+
+ r = pattern_compile("[[:upper:]]", 0, &cs);
+ if (r < 0)
+ return r;
+
+ r = pcre2_match(cs, (PCRE2_SPTR8) arg_pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
+ has_case = r >= 0;
+
+ flags = !has_case * PCRE2_CASELESS;
+ }
+
+ log_debug("Doing case %s matching based on %s",
+ flags & PCRE2_CASELESS ? "insensitive" : "sensitive",
+ arg_case_sensitive >= 0 ? "request" : "pattern casing");
+
+ r = pattern_compile(arg_pattern, flags, &arg_compiled_pattern);
+ if (r < 0)
+ return r;
+ }
+#endif
+
return 1;
}
@@ -2257,7 +2358,7 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto finish;
- if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
+ if (DEBUG_LOGGING) {
_cleanup_free_ char *filter;
filter = journal_make_match_string(j);
@@ -2415,6 +2516,7 @@ int main(int argc, char *argv[]) {
for (;;) {
while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
int flags;
+ size_t highlight[2] = {};
if (need_seek) {
if (!arg_reverse)
@@ -2468,6 +2570,58 @@ int main(int argc, char *argv[]) {
}
}
+#if HAVE_PCRE2
+ if (arg_compiled_pattern) {
+ _cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
+ const void *message;
+ size_t len;
+ PCRE2_SIZE *ovec;
+
+ md = pcre2_match_data_create(1, NULL);
+ if (!md)
+ return log_oom();
+
+ r = sd_journal_get_data(j, "MESSAGE", &message, &len);
+ if (r < 0) {
+ if (r == -ENOENT) {
+ need_seek = true;
+ continue;
+ }
+
+ log_error_errno(r, "Failed to get MESSAGE field: %m");
+ goto finish;
+ }
+
+ assert_se(message = startswith(message, "MESSAGE="));
+
+ r = pcre2_match(arg_compiled_pattern,
+ message,
+ len - strlen("MESSAGE="),
+ 0, /* start at offset 0 in the subject */
+ 0, /* default options */
+ md,
+ NULL);
+ if (r == PCRE2_ERROR_NOMATCH) {
+ need_seek = true;
+ continue;
+ }
+ if (r < 0) {
+ unsigned char buf[LINE_MAX];
+ int r2;
+
+ r2 = pcre2_get_error_message(r, buf, sizeof buf);
+ log_error("Pattern matching failed: %s",
+ r2 < 0 ? "unknown error" : (char*) buf);
+ r = -EINVAL;
+ goto finish;
+ }
+
+ ovec = pcre2_get_ovector_pointer(md);
+ highlight[0] = ovec[0];
+ highlight[1] = ovec[1];
+ }
+#endif
+
flags =
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH |
@@ -2476,7 +2630,8 @@ int main(int argc, char *argv[]) {
arg_utc * OUTPUT_UTC |
arg_no_hostname * OUTPUT_NO_HOSTNAME;
- r = output_journal(stdout, j, arg_output, 0, flags, arg_output_fields, &ellipsized);
+ r = output_journal(stdout, j, arg_output, 0, flags,
+ arg_output_fields, highlight, &ellipsized);
need_seek = true;
if (r == -EADDRNOTAVAIL)
break;
@@ -2527,5 +2682,10 @@ finish:
free(arg_root);
free(arg_verify_key);
+#if HAVE_PCRE2
+ if (arg_compiled_pattern)
+ pcre2_code_free(arg_compiled_pattern);
+#endif
+
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
index 65fb6ab63a..cee873215d 100644
--- a/src/journal/journald-native.c
+++ b/src/journal/journald-native.c
@@ -425,7 +425,7 @@ void server_process_native_file(
* https://github.com/systemd/systemd/issues/1822
*/
if (vfs.f_flag & ST_MANDLOCK) {
- log_error("Received file descriptor from file system with mandatory locking enable, refusing.");
+ log_error("Received file descriptor from file system with mandatory locking enabled, refusing.");
return;
}
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 48375f9c3e..5cd58e8a77 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -133,7 +133,7 @@ static int determine_path_usage(Server *s, const char *path, uint64_t *ret_used,
}
static void cache_space_invalidate(JournalStorageSpace *space) {
- memset(space, 0, sizeof(*space));
+ zero(*space);
}
static int cache_space_refresh(Server *s, JournalStorage *storage) {
@@ -241,6 +241,13 @@ void server_space_usage_message(Server *s, JournalStorage *storage) {
NULL);
}
+static bool uid_for_system_journal(uid_t uid) {
+
+ /* Returns true if the specified UID shall get its data stored in the system journal*/
+
+ return uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY;
+}
+
static void server_add_acls(JournalFile *f, uid_t uid) {
#if HAVE_ACL
int r;
@@ -248,7 +255,7 @@ static void server_add_acls(JournalFile *f, uid_t uid) {
assert(f);
#if HAVE_ACL
- if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY)
+ if (uid_for_system_journal(uid))
return;
r = add_acls_for_user(f->fd, uid);
@@ -406,7 +413,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
if (s->runtime_journal)
return s->runtime_journal;
- if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY)
+ if (uid_for_system_journal(uid))
return s->system_journal;
r = sd_id128_get_machine(&machine);
@@ -457,13 +464,14 @@ static int do_rotate(
return -EINVAL;
r = journal_file_rotate(f, s->compress, seal, s->deferred_closes);
- if (r < 0)
+ if (r < 0) {
if (*f)
- log_error_errno(r, "Failed to rotate %s: %m", (*f)->path);
+ return log_error_errno(r, "Failed to rotate %s: %m", (*f)->path);
else
- log_error_errno(r, "Failed to create new %s journal: %m", name);
- else
- server_add_acls(*f, uid);
+ return log_error_errno(r, "Failed to create new %s journal: %m", name);
+ }
+
+ server_add_acls(*f, uid);
return r;
}
@@ -1489,7 +1497,8 @@ static int server_open_hostname(Server *s) {
assert(s);
- s->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY);
+ s->hostname_fd = open("/proc/sys/kernel/hostname",
+ O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (s->hostname_fd < 0)
return log_error_errno(errno, "Failed to open /proc/sys/kernel/hostname: %m");
diff --git a/src/journal/journald.c b/src/journal/journald.c
index 6d670e2f4f..10a6955769 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -28,6 +28,7 @@
#include "journald-kmsg.h"
#include "journald-server.h"
#include "journald-syslog.h"
+#include "process-util.h"
#include "sigbus.h"
int main(int argc, char *argv[]) {
@@ -39,7 +40,8 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- log_set_target(LOG_TARGET_SAFE);
+ log_set_prohibit_ipc(true);
+ log_set_target(LOG_TARGET_AUTO);
log_set_facility(LOG_SYSLOG);
log_parse_environment();
log_open();
diff --git a/src/journal/meson.build b/src/journal/meson.build
index edb2a1a30e..a23f6a712c 100644
--- a/src/journal/meson.build
+++ b/src/journal/meson.build
@@ -15,7 +15,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with systemd; If not, see <http://www.gnu.org/licenses/>.
-journal_internal_sources = files('''
+journal_client_sources = files('''
audit-type.c
audit-type.h
catalog.c
@@ -38,14 +38,12 @@ journal_internal_sources = files('''
'''.split())
if conf.get('HAVE_GCRYPT') == 1
- journal_internal_sources += files('''
+ journal_client_sources += files('''
journal-authenticate.c
journal-authenticate.h
fsprg.c
fsprg.h
'''.split())
-
- journal_internal_sources += gcrypt_util_sources
endif
############################################################
@@ -71,7 +69,13 @@ audit_type_to_name = custom_target(
command : [awk, '-f', '@INPUT0@', '@INPUT1@'],
capture : true)
-journal_internal_sources += [audit_type_to_name]
+journal_client_sources += [audit_type_to_name]
+
+libjournal_client = static_library(
+ 'journal-client',
+ journal_client_sources,
+ include_directories : includes,
+ c_args : ['-fvisibility=default'])
############################################################
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 8a1b161d8f..6da7bf8e81 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -48,6 +48,7 @@
#include "lookup3.h"
#include "missing.h"
#include "path-util.h"
+#include "process-util.h"
#include "replace-var.h"
#include "stat-util.h"
#include "stdio-util.h"
diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c
index 409a876054..1f77197549 100644
--- a/src/journal/test-compress-benchmark.c
+++ b/src/journal/test-compress-benchmark.c
@@ -23,6 +23,7 @@
#include "env-util.h"
#include "macro.h"
#include "parse-util.h"
+#include "process-util.h"
#include "random-util.h"
#include "string-util.h"
#include "util.h"
diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c
index 67409cc918..89e68c7ac7 100644
--- a/src/libsystemd-network/arp-util.c
+++ b/src/libsystemd-network/arp-util.c
@@ -24,6 +24,7 @@
#include "arp-util.h"
#include "fd-util.h"
+#include "unaligned.h"
#include "util.h"
int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
@@ -48,12 +49,12 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
/* Sender Hardware Address must be different from our own */
- BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */
+ BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_ne32(&eth_mac->ether_addr_octet[0])),/* A <- 4 bytes of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */
- BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */
+ BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_ne16(&eth_mac->ether_addr_octet[4])),/* A <- remainder of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h
index ac5cc47efd..65c182f48b 100644
--- a/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/libsystemd-network/dhcp-lease-internal.h
@@ -34,6 +34,8 @@ struct sd_dhcp_route {
struct in_addr dst_addr;
struct in_addr gw_addr;
unsigned char dst_prefixlen;
+
+ uint8_t option;
};
struct sd_dhcp_raw_option {
diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c
index 010090aef1..602bf08a60 100644
--- a/src/libsystemd-network/dhcp-network.c
+++ b/src/libsystemd-network/dhcp-network.c
@@ -32,6 +32,7 @@
#include "dhcp-internal.h"
#include "fd-util.h"
#include "socket-util.h"
+#include "unaligned.h"
static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
uint32_t xid, const uint8_t *mac_addr,
@@ -70,13 +71,13 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((unsigned int *) eth_mac))), /* A <- 4 bytes of client's MAC */
+ BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])), /* A <- 4 bytes of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((unsigned short *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */
+ BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])), /* A <- remainder of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index cb5b359cbe..13844a86c6 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -29,25 +29,65 @@
#include "macro.h"
#include "sparse-endian.h"
+/* Common option header */
+typedef struct DHCP6Option {
+ be16_t code;
+ be16_t len;
+ uint8_t data[];
+} _packed_ DHCP6Option;
+
+/* Address option */
+struct iaaddr {
+ struct in6_addr address;
+ be32_t lifetime_preferred;
+ be32_t lifetime_valid;
+} _packed_;
+
+/* Prefix Delegation Prefix option */
+struct iapdprefix {
+ be32_t lifetime_preferred;
+ be32_t lifetime_valid;
+ uint8_t prefixlen;
+ struct in6_addr address;
+} _packed_;
+
typedef struct DHCP6Address DHCP6Address;
struct DHCP6Address {
LIST_FIELDS(DHCP6Address, addresses);
- struct {
- struct in6_addr address;
- be32_t lifetime_preferred;
- be32_t lifetime_valid;
- } iaaddr _packed_;
+ union {
+ struct iaaddr iaaddr;
+ struct iapdprefix iapdprefix;
+ };
};
+/* Non-temporary Address option */
+struct ia_na {
+ be32_t id;
+ be32_t lifetime_t1;
+ be32_t lifetime_t2;
+} _packed_;
+
+/* Prefix Delegation option */
+struct ia_pd {
+ be32_t id;
+ be32_t lifetime_t1;
+ be32_t lifetime_t2;
+} _packed_;
+
+/* Temporary Address option */
+struct ia_ta {
+ be32_t id;
+} _packed_;
+
struct DHCP6IA {
uint16_t type;
- struct {
- be32_t id;
- be32_t lifetime_t1;
- be32_t lifetime_t2;
- } _packed_;
+ union {
+ struct ia_na ia_na;
+ struct ia_pd ia_pd;
+ struct ia_ta ia_ta;
+ };
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
@@ -62,11 +102,12 @@ typedef struct DHCP6IA DHCP6IA;
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
size_t optlen, const void *optval);
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia);
+int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd);
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue);
-int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
- DHCP6IA *ia);
+int dhcp6_option_parse_status(DHCP6Option *option);
+int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia);
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
struct in6_addr **addrs, size_t count,
size_t *allocated);
diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h
index a3f00d4a5d..45e0e82427 100644
--- a/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/libsystemd-network/dhcp6-lease-internal.h
@@ -36,8 +36,10 @@ struct sd_dhcp6_lease {
bool rapid_commit;
DHCP6IA ia;
+ DHCP6IA pd;
DHCP6Address *addr_iter;
+ DHCP6Address *prefix_iter;
struct in6_addr *dns;
size_t dns_count;
diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
index f346bda5fc..df96ad739d 100644
--- a/src/libsystemd-network/dhcp6-option.c
+++ b/src/libsystemd-network/dhcp6-option.c
@@ -26,6 +26,7 @@
#include "alloc-util.h"
#include "dhcp6-internal.h"
+#include "dhcp6-lease-internal.h"
#include "dhcp6-protocol.h"
#include "dns-domain.h"
#include "sparse-endian.h"
@@ -33,14 +34,27 @@
#include "unaligned.h"
#include "util.h"
-#define DHCP6_OPTION_IA_NA_LEN 12
-#define DHCP6_OPTION_IA_TA_LEN 4
+typedef struct DHCP6StatusOption {
+ struct DHCP6Option option;
+ be16_t status;
+ char msg[];
+} _packed_ DHCP6StatusOption;
-typedef struct DHCP6Option {
- be16_t code;
- be16_t len;
- uint8_t data[];
-} _packed_ DHCP6Option;
+typedef struct DHCP6AddressOption {
+ struct DHCP6Option option;
+ struct iaaddr iaaddr;
+ uint8_t options[];
+} _packed_ DHCP6AddressOption;
+
+typedef struct DHCP6PDPrefixOption {
+ struct DHCP6Option option;
+ struct iapdprefix iapdprefix;
+ uint8_t options[];
+} _packed_ DHCP6PDPrefixOption;
+
+#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
+#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
+#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
size_t optlen) {
@@ -83,7 +97,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
uint16_t len;
uint8_t *ia_hdr;
- size_t ia_buflen, ia_addrlen = 0;
+ size_t iaid_offset, ia_buflen, ia_addrlen = 0;
DHCP6Address *addr;
int r;
@@ -92,10 +106,12 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
switch (ia->type) {
case SD_DHCP6_OPTION_IA_NA:
len = DHCP6_OPTION_IA_NA_LEN;
+ iaid_offset = offsetof(DHCP6IA, ia_na);
break;
case SD_DHCP6_OPTION_IA_TA:
len = DHCP6_OPTION_IA_TA_LEN;
+ iaid_offset = offsetof(DHCP6IA, ia_ta);
break;
default:
@@ -111,7 +127,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
*buf += sizeof(DHCP6Option);
*buflen -= sizeof(DHCP6Option);
- memcpy(*buf, &ia->id, len);
+ memcpy(*buf, (char*) ia + iaid_offset, len);
*buf += len;
*buflen -= len;
@@ -164,6 +180,42 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
return r;
}
+int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd) {
+ DHCP6Option *option = (DHCP6Option *)buf;
+ size_t i = sizeof(*option) + sizeof(pd->ia_pd);
+ DHCP6Address *prefix;
+
+ assert_return(buf, -EINVAL);
+ assert_return(pd, -EINVAL);
+ assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
+
+ if (len < i)
+ return -ENOBUFS;
+
+ option->code = htobe16(SD_DHCP6_OPTION_IA_PD);
+
+ memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd));
+
+ LIST_FOREACH(addresses, prefix, pd->addresses) {
+ DHCP6PDPrefixOption *prefix_opt;
+
+ if (len < i + sizeof(*prefix_opt))
+ return -ENOBUFS;
+
+ prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
+ prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
+ prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
+
+ memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix,
+ sizeof(struct iapdprefix));
+
+ i += sizeof(*prefix_opt);
+ }
+
+ option->len = htobe16(i - sizeof(*option));
+
+ return i;
+}
static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
DHCP6Option *option = (DHCP6Option*) *buf;
@@ -210,35 +262,147 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
return 0;
}
-int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
- DHCP6IA *ia) {
+int dhcp6_option_parse_status(DHCP6Option *option) {
+ DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
+
+ if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*statusopt))
+ return -ENOBUFS;
+
+ return be16toh(statusopt->status);
+}
+
+static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
+ uint32_t *lifetime_valid) {
+ DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
+ DHCP6Address *addr;
+ uint32_t lt_valid, lt_pref;
int r;
- uint16_t opt, status;
- size_t optlen;
+
+ if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*addr_option))
+ return -ENOBUFS;
+
+ lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
+ lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
+
+ if (lt_valid == 0 || lt_pref > lt_valid) {
+ log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d",
+ lt_pref, lt_valid);
+
+ return 0;
+ }
+
+ if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) {
+ r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options);
+ if (r != 0)
+ return r < 0 ? r: 0;
+ }
+
+ addr = new0(DHCP6Address, 1);
+ if (!addr)
+ return -ENOMEM;
+
+ LIST_INIT(addresses, addr);
+ memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr));
+
+ LIST_PREPEND(addresses, ia->addresses, addr);
+
+ *lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
+
+ return 0;
+}
+
+static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
+ uint32_t *lifetime_valid) {
+ DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
+ DHCP6Address *prefix;
+ uint32_t lt_valid, lt_pref;
+ int r;
+
+ if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*pdprefix_option))
+ return -ENOBUFS;
+
+ lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
+ lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
+
+ if (lt_valid == 0 || lt_pref > lt_valid) {
+ log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d",
+ lt_pref, lt_valid);
+
+ return 0;
+ }
+
+ if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) {
+ r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options);
+ if (r != 0)
+ return r < 0 ? r: 0;
+ }
+
+ prefix = new0(DHCP6Address, 1);
+ if (!prefix)
+ return -ENOMEM;
+
+ LIST_INIT(addresses, prefix);
+ memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix));
+
+ LIST_PREPEND(addresses, ia->addresses, prefix);
+
+ *lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
+
+ return 0;
+}
+
+int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
+ uint16_t iatype, optlen;
+ size_t i, len;
+ int r = 0, status;
+ uint16_t opt;
size_t iaaddr_offset;
- DHCP6Address *addr;
- uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0;
+ uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
assert_return(ia, -EINVAL);
assert_return(!ia->addresses, -EINVAL);
+ iatype = be16toh(iaoption->code);
+ len = be16toh(iaoption->len);
+
switch (iatype) {
case SD_DHCP6_OPTION_IA_NA:
- if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) +
- sizeof(addr->iaaddr)) {
+ if (len < DHCP6_OPTION_IA_NA_LEN) {
r = -ENOBUFS;
goto error;
}
iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
- memcpy(&ia->id, *buf, iaaddr_offset);
+ memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
- lt_t1 = be32toh(ia->lifetime_t1);
- lt_t2 = be32toh(ia->lifetime_t2);
+ lt_t1 = be32toh(ia->ia_na.lifetime_t1);
+ lt_t2 = be32toh(ia->ia_na.lifetime_t2);
if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
- log_dhcp6_client(client, "IA T1 %ds > T2 %ds",
+ log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds",
+ lt_t1, lt_t2);
+ r = -EINVAL;
+ goto error;
+ }
+
+ break;
+
+ case SD_DHCP6_OPTION_IA_PD:
+
+ if (len < sizeof(ia->ia_pd)) {
+ r = -ENOBUFS;
+ goto error;
+ }
+
+ iaaddr_offset = sizeof(ia->ia_pd);
+ memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
+
+ lt_t1 = be32toh(ia->ia_pd.lifetime_t1);
+ lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
+
+ if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
+ log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds",
lt_t1, lt_t2);
r = -EINVAL;
goto error;
@@ -247,17 +411,13 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
break;
case SD_DHCP6_OPTION_IA_TA:
- if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) +
- sizeof(addr->iaaddr)) {
+ if (len < DHCP6_OPTION_IA_TA_LEN) {
r = -ENOBUFS;
goto error;
}
iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
- memcpy(&ia->id, *buf, iaaddr_offset);
-
- ia->lifetime_t1 = 0;
- ia->lifetime_t2 = 0;
+ memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
break;
@@ -267,48 +427,63 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
}
ia->type = iatype;
+ i = iaaddr_offset;
- *buflen -= iaaddr_offset;
- *buf += iaaddr_offset;
+ while (i < len) {
+ DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
- while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) {
+ if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) {
+ r = -ENOBUFS;
+ goto error;
+ }
+
+ opt = be16toh(option->code);
+ optlen = be16toh(option->len);
switch (opt) {
case SD_DHCP6_OPTION_IAADDR:
- addr = new0(DHCP6Address, 1);
- if (!addr) {
- r = -ENOMEM;
+ if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
+ log_dhcp6_client(client, "IA Address option not in IA NA or TA option");
+ r = -EINVAL;
goto error;
}
- LIST_INIT(addresses, addr);
+ r = dhcp6_option_parse_address(option, ia, &lt_valid);
+ if (r < 0)
+ goto error;
+
+ if (lt_valid < lt_min)
+ lt_min = lt_valid;
- memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr));
+ break;
- lt_valid = be32toh(addr->iaaddr.lifetime_valid);
- lt_pref = be32toh(addr->iaaddr.lifetime_valid);
+ case SD_DHCP6_OPTION_IA_PD_PREFIX:
- if (!lt_valid || lt_pref > lt_valid) {
- log_dhcp6_client(client, "IA preferred %ds > valid %ds",
- lt_pref, lt_valid);
- free(addr);
- } else {
- LIST_PREPEND(addresses, ia->addresses, addr);
- if (lt_valid < lt_min)
- lt_min = lt_valid;
+ if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) {
+ log_dhcp6_client(client, "IA PD Prefix option not in IA PD option");
+ r = -EINVAL;
+ goto error;
}
+ r = dhcp6_option_parse_pdprefix(option, ia, &lt_valid);
+ if (r < 0)
+ goto error;
+
+ if (lt_valid < lt_min)
+ lt_min = lt_valid;
+
break;
case SD_DHCP6_OPTION_STATUS_CODE:
- if (optlen < sizeof(status))
- break;
- status = (*buf)[0] << 8 | (*buf)[1];
+ status = dhcp6_option_parse_status(option);
if (status) {
log_dhcp6_client(client, "IA status %d",
status);
+
+ dhcp6_lease_free_ia(ia);
+
r = -EINVAL;
goto error;
}
@@ -320,30 +495,41 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
break;
}
- *buflen -= optlen;
- *buf += optlen;
+ i += sizeof(*option) + optlen;
}
- if (r == -ENOMSG)
- r = 0;
+ switch(iatype) {
+ case SD_DHCP6_OPTION_IA_NA:
+ if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) {
+ lt_t1 = lt_min / 2;
+ lt_t2 = lt_min / 10 * 8;
+ ia->ia_na.lifetime_t1 = htobe32(lt_t1);
+ ia->ia_na.lifetime_t2 = htobe32(lt_t2);
- if (!ia->lifetime_t1 && !ia->lifetime_t2) {
- lt_t1 = lt_min / 2;
- lt_t2 = lt_min / 10 * 8;
- ia->lifetime_t1 = htobe32(lt_t1);
- ia->lifetime_t2 = htobe32(lt_t2);
+ log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero",
+ lt_t1, lt_t2);
+ }
- log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero",
- lt_t1, lt_t2);
- }
+ break;
- if (*buflen)
- r = -ENOMSG;
+ case SD_DHCP6_OPTION_IA_PD:
+ if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) {
+ lt_t1 = lt_min / 2;
+ lt_t2 = lt_min / 10 * 8;
+ ia->ia_pd.lifetime_t1 = htobe32(lt_t1);
+ ia->ia_pd.lifetime_t2 = htobe32(lt_t2);
-error:
- *buf += *buflen;
- *buflen = 0;
+ log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero",
+ lt_t1, lt_t2);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+error:
return r;
}
diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index 7bbf183996..5f7e809ba1 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -34,6 +34,7 @@ struct DHCP6Message {
} _packed_;
be32_t transaction_id;
};
+ uint8_t options[];
} _packed_;
typedef struct DHCP6Message DHCP6Message;
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index e48b7d22dd..94386e4860 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -22,6 +22,7 @@
#include <linux/if.h>
#include <netinet/ether.h>
+#include "sd-id128.h"
#include "sd-ndisc.h"
#include "alloc-util.h"
@@ -116,7 +117,8 @@ bool net_match_config(const struct ether_addr *match_mac,
char * const *match_names,
Condition *match_host,
Condition *match_virt,
- Condition *match_kernel,
+ Condition *match_kernel_cmdline,
+ Condition *match_kernel_version,
Condition *match_arch,
const struct ether_addr *dev_mac,
const char *dev_path,
@@ -131,7 +133,10 @@ bool net_match_config(const struct ether_addr *match_mac,
if (match_virt && condition_test(match_virt) <= 0)
return false;
- if (match_kernel && condition_test(match_kernel) <= 0)
+ if (match_kernel_cmdline && condition_test(match_kernel_cmdline) <= 0)
+ return false;
+
+ if (match_kernel_version && condition_test(match_kernel_version) <= 0)
return false;
if (match_arch && condition_test(match_arch) <= 0)
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index a54adac602..4e69f1a598 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -37,7 +37,8 @@ bool net_match_config(const struct ether_addr *match_mac,
char * const *match_name,
Condition *match_host,
Condition *match_virt,
- Condition *match_kernel,
+ Condition *match_kernel_cmdline,
+ Condition *match_kernel_version,
Condition *match_arch,
const struct ether_addr *dev_mac,
const char *dev_path,
diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h
index 441939b717..837e7f2603 100644
--- a/src/libsystemd-network/radv-internal.h
+++ b/src/libsystemd-network/radv-internal.h
@@ -93,6 +93,9 @@ struct sd_radv_prefix {
} _packed_ opt;
LIST_FIELDS(struct sd_radv_prefix, prefix);
+
+ usec_t valid_until;
+ usec_t preferred_until;
};
#define log_radv_full(level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index 78b8e058b4..2e88e39878 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -471,6 +471,7 @@ static int lease_parse_routes(
struct sd_dhcp_route *route = *routes + *routes_size;
int r;
+ route->option = SD_DHCP_OPTION_STATIC_ROUTE;
r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
if (r < 0) {
log_debug("Failed to determine destination prefix length from class based IP, ignoring");
@@ -514,6 +515,7 @@ static int lease_parse_classless_routes(
return -ENOMEM;
route = *routes + *routes_size;
+ route->option = SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE;
dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
route->dst_prefixlen = *option;
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index 63fb355e85..907b72391b 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -28,6 +28,7 @@
#include "dhcp-server-internal.h"
#include "fd-util.h"
#include "in-addr-util.h"
+#include "sd-id128.h"
#include "siphash24.h"
#include "string-util.h"
#include "unaligned.h"
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 1c12e5430f..ec3484383b 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -54,6 +54,8 @@ struct sd_dhcp6_client {
size_t mac_addr_len;
uint16_t arp_type;
DHCP6IA ia_na;
+ DHCP6IA ia_pd;
+ bool prefix_delegation;
be32_t transaction_id;
usec_t transaction_start;
struct sd_dhcp6_lease *lease;
@@ -230,7 +232,8 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
assert_return(client, -EINVAL);
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
- client->ia_na.id = htobe32(iaid);
+ client->ia_na.ia_na.id = htobe32(iaid);
+ client->ia_pd.ia_pd.id = htobe32(iaid);
return 0;
}
@@ -279,6 +282,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
case SD_DHCP6_OPTION_DOMAIN_LIST:
case SD_DHCP6_OPTION_SNTP_SERVERS:
case SD_DHCP6_OPTION_NTP_SERVER:
+ case SD_DHCP6_OPTION_RAPID_COMMIT:
break;
default:
@@ -298,6 +302,14 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
return 0;
}
+int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, bool delegation) {
+ assert_return(client, -EINVAL);
+
+ client->prefix_delegation = delegation;
+
+ return 0;
+}
+
int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
assert_return(client, -EINVAL);
@@ -336,8 +348,6 @@ static int client_reset(sd_dhcp6_client *client) {
client->receive_message =
sd_event_source_unref(client->receive_message);
- client->fd = safe_close(client->fd);
-
client->transaction_id = 0;
client->transaction_start = 0;
@@ -413,6 +423,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
+ if (client->prefix_delegation) {
+ r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd);
+ if (r < 0)
+ return r;
+
+ opt += r;
+ optlen -= r;
+ }
+
break;
case DHCP6_STATE_REQUEST:
@@ -439,6 +458,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
+ if (client->prefix_delegation) {
+ r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
+ if (r < 0)
+ return r;
+
+ opt += r;
+ optlen -= r;
+ }
+
break;
case DHCP6_STATE_REBIND:
@@ -454,6 +482,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
+ if (client->prefix_delegation) {
+ r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
+ if (r < 0)
+ return r;
+
+ opt += r;
+ optlen -= r;
+ }
+
break;
case DHCP6_STATE_STOPPED:
@@ -709,16 +746,20 @@ error:
static int client_ensure_iaid(sd_dhcp6_client *client) {
int r;
+ be32_t iaid;
assert(client);
- if (client->ia_na.id)
+ if (client->ia_na.ia_na.id)
return 0;
- r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
+ r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &iaid);
if (r < 0)
return r;
+ client->ia_na.ia_na.id = iaid;
+ client->ia_pd.ia_pd.id = iaid;
+
return 0;
}
@@ -727,23 +768,35 @@ static int client_parse_message(
DHCP6Message *message,
size_t len,
sd_dhcp6_lease *lease) {
+ size_t pos = 0;
int r;
- uint8_t *optval, *option, *id = NULL;
- uint16_t optcode, status;
- size_t optlen, id_len;
bool clientid = false;
- be32_t iaid_lease;
+ uint8_t *id = NULL;
+ size_t id_len;
+ uint32_t lt_t1 = ~0, lt_t2 = ~0;
assert(client);
assert(message);
assert(len >= sizeof(DHCP6Message));
assert(lease);
- option = (uint8_t *)message + sizeof(DHCP6Message);
len -= sizeof(DHCP6Message);
- while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
- &optval)) >= 0) {
+ while (pos < len) {
+ DHCP6Option *option = (DHCP6Option *)&message->options[pos];
+ uint16_t optcode, optlen;
+ int status;
+ uint8_t *optval;
+ be32_t iaid_lease;
+
+ if (len < offsetof(DHCP6Option, data) ||
+ len < offsetof(DHCP6Option, data) + be16toh(option->len))
+ return -ENOBUFS;
+
+ optcode = be16toh(option->code);
+ optlen = be16toh(option->len);
+ optval = option->data;
+
switch (optcode) {
case SD_DHCP6_OPTION_CLIENTID:
if (clientid) {
@@ -781,21 +834,21 @@ static int client_parse_message(
if (optlen != 1)
return -EINVAL;
- r = dhcp6_lease_set_preference(lease, *optval);
+ r = dhcp6_lease_set_preference(lease, optval[0]);
if (r < 0)
return r;
break;
case SD_DHCP6_OPTION_STATUS_CODE:
- if (optlen < 2)
- return -EINVAL;
-
- status = optval[0] << 8 | optval[1];
+ status = dhcp6_option_parse_status(option);
if (status) {
log_dhcp6_client(client, "%s Status %s",
dhcp6_message_type_to_string(message->type),
dhcp6_message_status_to_string(status));
+ dhcp6_lease_free_ia(&lease->ia);
+ dhcp6_lease_free_ia(&lease->pd);
+
return -EINVAL;
}
@@ -808,8 +861,35 @@ static int client_parse_message(
break;
}
- r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
- &lease->ia);
+ r = dhcp6_option_parse_ia(option, &lease->ia);
+ if (r < 0 && r != -ENOMSG)
+ return r;
+
+ r = dhcp6_lease_get_iaid(lease, &iaid_lease);
+ if (r < 0)
+ return r;
+
+ if (client->ia_na.ia_na.id != iaid_lease) {
+ log_dhcp6_client(client, "%s has wrong IAID for IA NA",
+ dhcp6_message_type_to_string(message->type));
+ return -EINVAL;
+ }
+
+ if (lease->ia.addresses) {
+ lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
+ lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
+ }
+
+ break;
+
+ case SD_DHCP6_OPTION_IA_PD:
+ if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
+ log_dhcp6_client(client, "Information request ignoring IA PD option");
+
+ break;
+ }
+
+ r = dhcp6_option_parse_ia(option, &lease->pd);
if (r < 0 && r != -ENOMSG)
return r;
@@ -817,12 +897,17 @@ static int client_parse_message(
if (r < 0)
return r;
- if (client->ia_na.id != iaid_lease) {
- log_dhcp6_client(client, "%s has wrong IAID",
+ if (client->ia_pd.ia_pd.id != iaid_lease) {
+ log_dhcp6_client(client, "%s has wrong IAID for IA PD",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
+ if (lease->pd.addresses) {
+ lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
+ lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
+ }
+
break;
case SD_DHCP6_OPTION_RAPID_COMMIT:
@@ -861,12 +946,10 @@ static int client_parse_message(
break;
}
+ pos += sizeof(*option) + optlen;
}
- if (r == -ENOMSG)
- r = 0;
-
- if (r < 0 || !clientid) {
+ if (!clientid) {
log_dhcp6_client(client, "%s has incomplete options",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
@@ -877,6 +960,17 @@ static int client_parse_message(
if (r < 0)
log_dhcp6_client(client, "%s has no server id",
dhcp6_message_type_to_string(message->type));
+ return r;
+ }
+
+ if (lease->ia.addresses) {
+ lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
+ lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
+ }
+
+ if (lease->pd.addresses) {
+ lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
+ lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
}
return r;
@@ -1092,6 +1186,24 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
if (r < 0)
return r;
+ if (!client->receive_message) {
+ r = sd_event_add_io(client->event, &client->receive_message,
+ client->fd, EPOLLIN, client_receive_message,
+ client);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_priority(client->receive_message,
+ client->event_priority);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_description(client->receive_message,
+ "dhcp6-receive-message");
+ if (r < 0)
+ goto error;
+ }
+
switch (state) {
case DHCP6_STATE_STOPPED:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
@@ -1117,17 +1229,17 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
case DHCP6_STATE_BOUND:
- if (client->lease->ia.lifetime_t1 == 0xffffffff ||
- client->lease->ia.lifetime_t2 == 0xffffffff) {
+ if (client->lease->ia.ia_na.lifetime_t1 == 0xffffffff ||
+ client->lease->ia.ia_na.lifetime_t2 == 0xffffffff) {
log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
- be32toh(client->lease->ia.lifetime_t1),
- be32toh(client->lease->ia.lifetime_t2));
+ be32toh(client->lease->ia.ia_na.lifetime_t1),
+ be32toh(client->lease->ia.ia_na.lifetime_t2));
return 0;
}
- timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
+ timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t1) * USEC_PER_SEC);
log_dhcp6_client(client, "T1 expires in %s",
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
@@ -1138,18 +1250,18 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
10 * USEC_PER_SEC, client_timeout_t1,
client);
if (r < 0)
- return r;
+ goto error;
r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
client->event_priority);
if (r < 0)
- return r;
+ goto error;
r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
if (r < 0)
- return r;
+ goto error;
- timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
+ timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t2) * USEC_PER_SEC);
log_dhcp6_client(client, "T2 expires in %s",
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
@@ -1160,16 +1272,16 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
10 * USEC_PER_SEC, client_timeout_t2,
client);
if (r < 0)
- return r;
+ goto error;
r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
client->event_priority);
if (r < 0)
- return r;
+ goto error;
r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
if (r < 0)
- return r;
+ goto error;
client->state = state;
@@ -1183,18 +1295,22 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
client);
if (r < 0)
- return r;
+ goto error;
r = sd_event_source_set_priority(client->timeout_resend,
client->event_priority);
if (r < 0)
- return r;
+ goto error;
r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
if (r < 0)
- return r;
+ goto error;
return 0;
+
+ error:
+ client_reset(client);
+ return r;
}
int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
@@ -1202,6 +1318,8 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
+ client->fd = safe_close(client->fd);
+
return 0;
}
@@ -1235,32 +1353,18 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
if (r < 0)
return r;
- r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
- if (r < 0) {
- _cleanup_free_ char *p = NULL;
-
- (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
- return log_dhcp6_client_errno(client, r,
- "Failed to bind to UDP socket at address %s: %m", strna(p));
- }
-
- client->fd = r;
-
- r = sd_event_add_io(client->event, &client->receive_message,
- client->fd, EPOLLIN, client_receive_message,
- client);
- if (r < 0)
- goto error;
+ if (client->fd < 0) {
+ r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
+ if (r < 0) {
+ _cleanup_free_ char *p = NULL;
- r = sd_event_source_set_priority(client->receive_message,
- client->event_priority);
- if (r < 0)
- goto error;
+ (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
+ return log_dhcp6_client_errno(client, r,
+ "Failed to bind to UDP socket at address %s: %m", strna(p));
+ }
- r = sd_event_source_set_description(client->receive_message,
- "dhcp6-receive-message");
- if (r < 0)
- goto error;
+ client->fd = r;
+ }
if (client->information_request)
state = DHCP6_STATE_INFORMATION_REQUEST;
@@ -1270,10 +1374,6 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
"Managed");
return client_start(client, state);
-
-error:
- client_reset(client);
- return r;
}
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
@@ -1333,6 +1433,8 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
client_reset(client);
+ client->fd = safe_close(client->fd);
+
sd_dhcp6_client_detach_event(client);
free(client->req_opts);
@@ -1352,6 +1454,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
client->n_ref = 1;
client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
+ client->ia_pd.type = SD_DHCP6_OPTION_IA_PD;
client->ifindex = -1;
client->fd = -1;
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index 6f604e072f..1f3a782f8c 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -49,7 +49,7 @@ int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
valid = t;
}
- t = be32toh(ia->lifetime_t2);
+ t = be32toh(ia->ia_na.lifetime_t2);
if (t > valid)
return -EINVAL;
@@ -144,7 +144,7 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
assert_return(lease, -EINVAL);
assert_return(iaid, -EINVAL);
- *iaid = lease->ia.id;
+ *iaid = lease->ia.ia_na.id;
return 0;
}
@@ -176,6 +176,37 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
lease->addr_iter = lease->ia.addresses;
}
+int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
+ uint8_t *prefix_len,
+ uint32_t *lifetime_preferred,
+ uint32_t *lifetime_valid) {
+ assert_return(lease, -EINVAL);
+ assert_return(prefix, -EINVAL);
+ assert_return(prefix_len, -EINVAL);
+ assert_return(lifetime_preferred, -EINVAL);
+ assert_return(lifetime_valid, -EINVAL);
+
+ if (!lease->prefix_iter)
+ return -ENOMSG;
+
+ memcpy(prefix, &lease->prefix_iter->iapdprefix.address,
+ sizeof(struct in6_addr));
+ *prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
+ *lifetime_preferred =
+ be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
+ *lifetime_valid =
+ be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
+
+ lease->prefix_iter = lease->prefix_iter->addresses_next;
+
+ return 0;
+}
+
+void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
+ if (lease)
+ lease->prefix_iter = lease->pd.addresses;
+}
+
int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
int r;
@@ -382,6 +413,7 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
free(lease->serverid);
dhcp6_lease_free_ia(&lease->ia);
+ dhcp6_lease_free_ia(&lease->pd);
free(lease->dns);
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index 23e2f5211d..f3d09eb30a 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <string.h>
+#include "sd-id128.h"
#include "sd-ipv4acd.h"
#include "sd-ipv4ll.h"
diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c
index 46704acdef..f30d6164ea 100644
--- a/src/libsystemd-network/sd-radv.c
+++ b/src/libsystemd-network/sd-radv.c
@@ -21,7 +21,6 @@
#include <netinet/icmp6.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <linux/in6.h>
#include "sd-radv.h"
@@ -169,6 +168,12 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst,
.msg_namelen = sizeof(dst_addr),
.msg_iov = iov,
};
+ usec_t time_now;
+ int r;
+
+ r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0)
+ return r;
if (dst && !in_addr_is_null(AF_INET6, (union in_addr_union*) dst))
dst_addr.sin6_addr = *dst;
@@ -198,6 +203,18 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst,
}
LIST_FOREACH(prefix, p, ra->prefixes) {
+ if (p->valid_until) {
+
+ if (time_now > p->valid_until)
+ p->opt.valid_lifetime = 0;
+ else
+ p->opt.valid_lifetime = htobe32((p->valid_until - time_now) / USEC_PER_SEC);
+
+ if (time_now > p->preferred_until)
+ p->opt.preferred_lifetime = 0;
+ else
+ p->opt.preferred_lifetime = htobe32((p->preferred_until - time_now) / USEC_PER_SEC);
+ }
iov[msg.msg_iovlen].iov_base = &p->opt;
iov[msg.msg_iovlen].iov_len = sizeof(p->opt);
msg.msg_iovlen++;
@@ -446,9 +463,6 @@ _public_ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
assert_return(ra, -EINVAL);
assert_return(mtu >= 1280, -EINVAL);
- if (ra->state != SD_RADV_STATE_IDLE)
- return -EBUSY;
-
ra->mtu = mtu;
return 0;
@@ -518,9 +532,13 @@ _public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
return r;
}
-_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
+_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, bool dynamic) {
sd_radv_prefix *cur;
+ int r;
_cleanup_free_ char *addr_p = NULL;
+ char time_string_preferred[FORMAT_TIMESPAN_MAX];
+ char time_string_valid[FORMAT_TIMESPAN_MAX];
+ usec_t time_now, valid, preferred, valid_until, preferred_until;
assert_return(ra, -EINVAL);
@@ -528,7 +546,6 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
return -EINVAL;
LIST_FOREACH(prefix, cur, ra->prefixes) {
- int r;
r = in_addr_prefix_intersect(AF_INET6,
(union in_addr_union*) &cur->opt.in6_addr,
@@ -539,12 +556,15 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
_cleanup_free_ char *addr_cur = NULL;
(void) in_addr_to_string(AF_INET6,
- (union in_addr_union*) &cur->opt.in6_addr,
- &addr_cur);
- (void) in_addr_to_string(AF_INET6,
(union in_addr_union*) &p->opt.in6_addr,
&addr_p);
+ if (dynamic && cur->opt.prefixlen == p->opt.prefixlen)
+ goto update;
+
+ (void) in_addr_to_string(AF_INET6,
+ (union in_addr_union*) &cur->opt.in6_addr,
+ &addr_cur);
log_radv("IPv6 prefix %s/%u already configured, ignoring %s/%u",
addr_cur, cur->opt.prefixlen,
addr_p, p->opt.prefixlen);
@@ -560,11 +580,69 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
ra->n_prefixes++;
(void) in_addr_to_string(AF_INET6, (union in_addr_union*) &p->opt.in6_addr, &addr_p);
- log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen);
+
+ if (!dynamic) {
+ log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen);
+ return 0;
+ }
+
+ cur = p;
+
+ update:
+ r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0)
+ return r;
+
+ valid = be32toh(p->opt.valid_lifetime) * USEC_PER_SEC;
+ valid_until = usec_add(valid, time_now);
+ if (valid_until == USEC_INFINITY)
+ return -EOVERFLOW;
+
+ preferred = be32toh(p->opt.preferred_lifetime) * USEC_PER_SEC;
+ preferred_until = usec_add(preferred, time_now);
+ if (preferred_until == USEC_INFINITY)
+ return -EOVERFLOW;
+
+ cur->valid_until = valid_until;
+ cur->preferred_until = preferred_until;
+
+ log_radv("%s prefix %s/%u preferred %s valid %s",
+ cur? "Updated": "Added",
+ addr_p, p->opt.prefixlen,
+ format_timespan(time_string_preferred, FORMAT_TIMESPAN_MAX,
+ preferred, USEC_PER_SEC),
+ format_timespan(time_string_valid, FORMAT_TIMESPAN_MAX,
+ valid, USEC_PER_SEC));
return 0;
}
+_public_ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra,
+ struct in6_addr *prefix,
+ uint8_t prefixlen) {
+ sd_radv_prefix *cur, *next;
+
+ assert_return(ra, NULL);
+ assert_return(prefix, NULL);
+
+ LIST_FOREACH_SAFE(prefix, cur, next, ra->prefixes) {
+ if (prefixlen != cur->opt.prefixlen)
+ continue;
+
+ if (!in_addr_equal(AF_INET6,
+ (union in_addr_union *)prefix,
+ (union in_addr_union *)&cur->opt.in6_addr))
+ continue;
+
+ LIST_REMOVE(prefix, ra->prefixes, cur);
+ ra->n_prefixes--;
+
+ break;
+ }
+
+ return cur;
+}
+
_public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
const struct in6_addr *dns, size_t n_dns) {
_cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL;
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index a0418ecdb9..aae49fa467 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -156,6 +156,138 @@ static int test_option(sd_event *e) {
return 0;
}
+static int test_option_status(sd_event *e) {
+ uint8_t option1[] = {
+ /* IA NA */
+ 0x00, 0x03, 0x00, 0x12, 0x1a, 0x1d, 0x1a, 0x1d,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
+ /* status option */
+ 0x00, 0x0d, 0x00, 0x02, 0x00, 0x01,
+ };
+ static const uint8_t option2[] = {
+ /* IA NA */
+ 0x00, 0x03, 0x00, 0x2e, 0x1a, 0x1d, 0x1a, 0x1d,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
+ /* IA Addr */
+ 0x00, 0x05, 0x00, 0x1e,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d,
+ /* status option */
+ 0x00, 0x0d, 0x00, 0x02, 0x00, 0x01,
+ };
+ static const uint8_t option3[] = {
+ /* IA NA */
+ 0x00, 0x03, 0x00, 0x34, 0x1a, 0x1d, 0x1a, 0x1d,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
+ /* IA Addr */
+ 0x00, 0x05, 0x00, 0x24,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d,
+ /* status option */
+ 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 'f', 'o',
+ 'o', 'b', 'a', 'r',
+ };
+ static const uint8_t option4[] = {
+ /* IA PD */
+ 0x00, 0x19, 0x00, 0x2f, 0x1a, 0x1d, 0x1a, 0x1d,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
+ /* IA PD Prefix */
+ 0x00, 0x1a, 0x00, 0x1f,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe,
+ 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00,
+ /* status option */
+ 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
+ };
+ static const uint8_t option5[] = {
+ /* IA PD */
+ 0x00, 0x19, 0x00, 0x52, 0x1a, 0x1d, 0x1a, 0x1d,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
+ /* IA PD Prefix #1 */
+ 0x00, 0x1a, 0x00, 0x1f,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe,
+ 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00,
+ /* status option */
+ 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
+ /* IA PD Prefix #2 */
+ 0x00, 0x1a, 0x00, 0x1f,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x0l, 0xd0,
+ 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00,
+ 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
+ };
+ DHCP6Option *option;
+ DHCP6IA ia, pd;
+ int r = 0;
+
+ if (verbose)
+ printf("* %s\n", __FUNCTION__);
+
+ zero(ia);
+ option = (DHCP6Option *)option1;
+ assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len));
+
+ r = dhcp6_option_parse_ia(option, &ia);
+ assert_se(r == -EINVAL);
+ assert_se(ia.addresses == NULL);
+
+ option->len = htobe16(17);
+ r = dhcp6_option_parse_ia(option, &ia);
+ assert_se(r == -ENOBUFS);
+ assert_se(ia.addresses == NULL);
+
+ option->len = htobe16(sizeof(DHCP6Option));
+ r = dhcp6_option_parse_ia(option, &ia);
+ assert_se(r == -ENOBUFS);
+ assert_se(ia.addresses == NULL);
+
+ zero(ia);
+ option = (DHCP6Option *)option2;
+ assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len));
+
+ r = dhcp6_option_parse_ia(option, &ia);
+ assert_se(r >= 0);
+ assert_se(ia.addresses == NULL);
+
+ zero(ia);
+ option = (DHCP6Option *)option3;
+ assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len));
+
+ r = dhcp6_option_parse_ia(option, &ia);
+ assert_se(r >= 0);
+ assert_se(ia.addresses != NULL);
+ dhcp6_lease_free_ia(&ia);
+
+ zero(pd);
+ option = (DHCP6Option *)option4;
+ assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len));
+
+ r = dhcp6_option_parse_ia(option, &pd);
+ assert_se(r == 0);
+ assert_se(pd.addresses != NULL);
+ assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0);
+ assert_se(memcmp(&pd.ia_pd.lifetime_t1, &option4[8], 4) == 0);
+ assert_se(memcmp(&pd.ia_pd.lifetime_t2, &option4[12], 4) == 0);
+ dhcp6_lease_free_ia(&pd);
+
+ zero(pd);
+ option = (DHCP6Option *)option5;
+ assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len));
+
+ r = dhcp6_option_parse_ia(option, &pd);
+ assert_se(r == 0);
+ assert_se(pd.addresses != NULL);
+ dhcp6_lease_free_ia(&pd);
+
+ return 0;
+}
+
static uint8_t msg_advertise[198] = {
0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e,
0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30,
@@ -217,14 +349,13 @@ static uint8_t fqdn_wire[16] = {
static int test_advertise_option(sd_event *e) {
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
- uint8_t *optval, *opt = msg_advertise + sizeof(DHCP6Message);
- uint16_t optcode;
- size_t optlen, len = sizeof(msg_advertise) - sizeof(DHCP6Message);
+ size_t len = sizeof(msg_advertise) - sizeof(DHCP6Message), pos = 0;
be32_t val;
uint8_t preference = 255;
struct in6_addr addr;
uint32_t lt_pref, lt_valid;
int r;
+ uint8_t *opt;
bool opt_clientid = false;
struct in6_addr *addrs;
char **domains;
@@ -232,14 +363,19 @@ static int test_advertise_option(sd_event *e) {
if (verbose)
printf("* %s\n", __FUNCTION__);
+ assert_se(len >= sizeof(DHCP6Message));
+
assert_se(dhcp6_lease_new(&lease) >= 0);
assert_se(advertise->type == DHCP6_ADVERTISE);
assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) ==
0x0fb4e5);
- while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen,
- &optval)) >= 0) {
+ while (pos < len) {
+ DHCP6Option *option = (DHCP6Option *)&advertise->options[pos];
+ const uint16_t optcode = be16toh(option->code);
+ const uint16_t optlen = be16toh(option->len);
+ uint8_t *optval = option->data;
switch(optcode) {
case SD_DHCP6_OPTION_CLIENTID:
@@ -261,9 +397,7 @@ static int test_advertise_option(sd_event *e) {
val = htobe32(120);
assert_se(!memcmp(optval + 8, &val, sizeof(val)));
- assert_se(dhcp6_option_parse_ia(&optval, &optlen,
- optcode,
- &lease->ia) >= 0);
+ assert_se(dhcp6_option_parse_ia(option, &lease->ia) >= 0);
break;
@@ -309,11 +443,11 @@ static int test_advertise_option(sd_event *e) {
default:
break;
}
- }
+ pos += sizeof(*option) + optlen;
+ }
- assert_se(r == -ENOMSG);
-
+ assert_se(pos == len);
assert_se(opt_clientid);
sd_dhcp6_lease_reset_address_iter(lease);
@@ -415,15 +549,11 @@ static int test_client_send_reply(DHCP6Message *request) {
return 0;
}
-static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
- size_t len) {
+static int test_client_verify_request(DHCP6Message *request, size_t len) {
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
- uint8_t *optval;
- uint16_t optcode;
- size_t optlen;
+ size_t pos = 0;
bool found_clientid = false, found_iana = false, found_serverid = false,
found_elapsed_time = false, found_fqdn = false;
- int r;
struct in6_addr addr;
be32_t val;
uint32_t lt_pref, lt_valid;
@@ -432,8 +562,14 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
assert_se(dhcp6_lease_new(&lease) >= 0);
- while ((r = dhcp6_option_parse(&option, &len,
- &optcode, &optlen, &optval)) >= 0) {
+ len -= sizeof(DHCP6Message);
+
+ while (pos < len) {
+ DHCP6Option *option = (DHCP6Option *)&request->options[pos];
+ uint16_t optcode = be16toh(option->code);
+ uint16_t optlen = be16toh(option->len);
+ uint8_t *optval = option->data;
+
switch(optcode) {
case SD_DHCP6_OPTION_CLIENTID:
assert_se(!found_clientid);
@@ -458,8 +594,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
val = htobe32(120);
assert_se(!memcmp(optval + 8, &val, sizeof(val)));
- assert_se(!dhcp6_option_parse_ia(&optval, &optlen,
- optcode, &lease->ia));
+ assert_se(!dhcp6_option_parse_ia(option, &lease->ia));
break;
@@ -489,9 +624,10 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire)));
break;
}
+
+ pos += sizeof(*option) + optlen;
}
- assert_se(r == -ENOMSG);
assert_se(found_clientid && found_iana && found_serverid &&
found_elapsed_time);
@@ -526,19 +662,21 @@ static int test_client_send_advertise(DHCP6Message *solicit) {
return 0;
}
-static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
- size_t len) {
- uint8_t *optval;
- uint16_t optcode;
- size_t optlen;
+static int test_client_verify_solicit(DHCP6Message *solicit, size_t len) {
bool found_clientid = false, found_iana = false,
found_elapsed_time = false, found_fqdn = false;
- int r;
+ size_t pos = 0;
assert_se(solicit->type == DHCP6_SOLICIT);
- while ((r = dhcp6_option_parse(&option, &len,
- &optcode, &optlen, &optval)) >= 0) {
+ len -= sizeof(DHCP6Message);
+
+ while (pos < len) {
+ DHCP6Option *option = (DHCP6Option *)&solicit->options[pos];
+ uint16_t optcode = be16toh(option->code);
+ uint16_t optlen = be16toh(option->len);
+ uint8_t *optval = option->data;
+
switch(optcode) {
case SD_DHCP6_OPTION_CLIENTID:
assert_se(!found_clientid);
@@ -578,9 +716,11 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
break;
}
+
+ pos += sizeof(*option) + optlen;
}
- assert_se(r == -ENOMSG);
+ assert_se(pos == len);
assert_se(found_clientid && found_iana && found_elapsed_time);
return 0;
@@ -623,17 +763,15 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event,
assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
assert_se(sd_dhcp6_client_start(client) >= 0);
+
}
static int test_client_verify_information_request(DHCP6Message *information_request,
- uint8_t *option, size_t len) {
+ size_t len) {
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
- uint8_t *optval;
- uint16_t optcode;
- size_t optlen;
+ size_t pos = 0;
bool found_clientid = false, found_elapsed_time = false;
- int r;
struct in6_addr addr;
uint32_t lt_pref, lt_valid;
@@ -641,8 +779,14 @@ static int test_client_verify_information_request(DHCP6Message *information_requ
assert_se(dhcp6_lease_new(&lease) >= 0);
- while ((r = dhcp6_option_parse(&option, &len,
- &optcode, &optlen, &optval)) >= 0) {
+ len -= sizeof(DHCP6Message);
+
+ while (pos < len) {
+ DHCP6Option *option = (DHCP6Option *)&information_request->options[pos];
+ uint16_t optcode = be16toh(option->code);
+ uint16_t optlen = be16toh(option->len);
+ uint8_t *optval = option->data;
+
switch(optcode) {
case SD_DHCP6_OPTION_CLIENTID:
assert_se(!found_clientid);
@@ -671,9 +815,11 @@ static int test_client_verify_information_request(DHCP6Message *information_requ
break;
}
+
+ pos += sizeof(*option) + optlen;
}
- assert_se(r == -ENOMSG);
+ assert_se(pos == len);
assert_se(found_clientid && found_elapsed_time);
sd_dhcp6_lease_reset_address_iter(lease);
@@ -689,7 +835,6 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
struct in6_addr mcast =
IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
DHCP6Message *message;
- uint8_t *option;
assert_se(s == test_dhcp_fd[0]);
assert_se(server_address);
@@ -699,21 +844,19 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
message = (DHCP6Message *)packet;
- option = (uint8_t *)(message + 1);
- len -= sizeof(DHCP6Message);
assert_se(message->transaction_id & 0x00ffffff);
if (test_client_message_num == 0) {
- test_client_verify_information_request(message, option, len);
+ test_client_verify_information_request(message, len);
test_client_send_reply(message);
test_client_message_num++;
} else if (test_client_message_num == 1) {
- test_client_verify_solicit(message, option, len);
+ test_client_verify_solicit(message, len);
test_client_send_advertise(message);
test_client_message_num++;
} else if (test_client_message_num == 2) {
- test_client_verify_request(message, option, len);
+ test_client_verify_request(message, len);
test_client_send_reply(message);
test_client_message_num++;
}
@@ -789,6 +932,7 @@ int main(int argc, char *argv[]) {
test_client_basic(e);
test_option(e);
+ test_option_status(e);
test_advertise_option(e);
test_client_solicit(e);
diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c
index c62689373f..b91797cb66 100644
--- a/src/libsystemd-network/test-lldp.c
+++ b/src/libsystemd-network/test-lldp.c
@@ -20,6 +20,7 @@
***/
#include <arpa/inet.h>
+#include <errno.h>
#include <net/ethernet.h>
#include <stdio.h>
#include <string.h>
diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c
index c1a8d5a00d..1fc8ca9eba 100644
--- a/src/libsystemd-network/test-ndisc-ra.c
+++ b/src/libsystemd-network/test-ndisc-ra.c
@@ -342,8 +342,8 @@ static void test_ra(void) {
if (prefix[i].preferred)
assert_se(sd_radv_prefix_set_preferred_lifetime(p, prefix[i].preferred) >= 0);
- assert_se((sd_radv_add_prefix(ra, p) >= 0) == prefix[i].succesful);
- assert_se(sd_radv_add_prefix(ra, p) < 0);
+ assert_se((sd_radv_add_prefix(ra, p, false) >= 0) == prefix[i].succesful);
+ assert_se(sd_radv_add_prefix(ra, p, false) < 0);
p = sd_radv_prefix_unref(p);
assert_se(!p);
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 1a29b03e85..00aeefbe19 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -530,3 +530,22 @@ global:
sd_bus_message_new;
sd_bus_message_seal;
} LIBSYSTEMD_234;
+
+LIBSYSTEMD_237 {
+global:
+ sd_bus_set_watch_bind;
+ sd_bus_get_watch_bind;
+ sd_bus_request_name_async;
+ sd_bus_release_name_async;
+ sd_bus_add_match_async;
+ sd_bus_match_signal;
+ sd_bus_match_signal_async;
+ sd_bus_is_ready;
+ sd_bus_set_connected_signal;
+ sd_bus_get_connected_signal;
+ sd_bus_set_sender;
+ sd_bus_get_sender;
+ sd_bus_message_set_sender;
+ sd_event_source_get_io_fd_own;
+ sd_event_source_set_io_fd_own;
+} LIBSYSTEMD_236;
diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build
index 4abf50b111..706e090762 100644
--- a/src/libsystemd/meson.build
+++ b/src/libsystemd/meson.build
@@ -17,9 +17,7 @@
sd_login_c = files('sd-login/sd-login.c')
-libsystemd_internal_sources = files('''
- sd-bus/bus-bloom.c
- sd-bus/bus-bloom.h
+libsystemd_sources = files('''
sd-bus/bus-common-errors.c
sd-bus/bus-common-errors.h
sd-bus/bus-container.c
@@ -74,6 +72,7 @@ libsystemd_internal_sources = files('''
sd-id128/id128-util.c
sd-id128/id128-util.h
sd-id128/sd-id128.c
+ sd-netlink/generic-netlink.c
sd-netlink/local-addresses.c
sd-netlink/local-addresses.h
sd-netlink/netlink-internal.h
@@ -93,14 +92,15 @@ libsystemd_internal_sources = files('''
sd-utf8/sd-utf8.c
'''.split()) + sd_login_c
-libsystemd_internal = static_library(
+libsystemd_static = static_library(
'systemd',
- libsystemd_internal_sources,
+ libsystemd_sources,
install : false,
include_directories : includes,
link_with : libbasic,
dependencies : [threads,
- librt])
+ librt],
+ c_args : ['-fvisibility=default'])
libsystemd_sym = 'src/libsystemd/libsystemd.sym'
diff --git a/src/libsystemd/sd-bus/bus-bloom.c b/src/libsystemd/sd-bus/bus-bloom.c
deleted file mode 100644
index ebda6516e2..0000000000
--- a/src/libsystemd/sd-bus/bus-bloom.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "bus-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-container.c b/src/libsystemd/sd-bus/bus-container.c
index 8f6d34838e..16156d8823 100644
--- a/src/libsystemd/sd-bus/bus-container.c
+++ b/src/libsystemd/sd-bus/bus-container.c
@@ -31,9 +31,8 @@
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;
+ pid_t child;
ssize_t n;
assert(b);
@@ -62,11 +61,10 @@ int bus_container_connect_socket(sd_bus *b) {
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
return -errno;
- child = fork();
- if (child < 0)
- return -errno;
-
- if (child == 0) {
+ r = safe_fork("(sd-buscntr)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ if (r < 0)
+ return r;
+ if (r == 0) {
pid_t grandchild;
pair[0] = safe_close(pair[0]);
@@ -82,11 +80,10 @@ int bus_container_connect_socket(sd_bus *b) {
* comes from a process from within the container, and
* not outside of it */
- grandchild = fork();
- if (grandchild < 0)
+ r = safe_fork("(sd-buscntr2)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &grandchild);
+ if (r < 0)
_exit(EXIT_FAILURE);
-
- if (grandchild == 0) {
+ if (r == 0) {
r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
if (r < 0) {
@@ -99,21 +96,20 @@ int bus_container_connect_socket(sd_bus *b) {
_exit(EXIT_SUCCESS);
}
- r = wait_for_terminate(grandchild, &si);
+ r = wait_for_terminate_and_check("(sd-buscntr2)", grandchild, 0);
if (r < 0)
_exit(EXIT_FAILURE);
- if (si.si_code != CLD_EXITED)
- _exit(EXIT_FAILURE);
-
- _exit(si.si_status);
+ _exit(r);
}
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate(child, &si);
+ r = wait_for_terminate_and_check("(sd-buscntr)", child, 0);
if (r < 0)
return r;
+ if (r != EXIT_SUCCESS)
+ return -EPROTO;
n = read(pair[0], &error_buf, sizeof(error_buf));
if (n < 0)
@@ -133,11 +129,5 @@ int bus_container_connect_socket(sd_bus *b) {
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);
}
diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c
index 12478e7cc6..9dd5274bf6 100644
--- a/src/libsystemd/sd-bus/bus-control.c
+++ b/src/libsystemd/sd-bus/bus-control.c
@@ -28,12 +28,12 @@
#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 "process-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
@@ -43,6 +43,7 @@ _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(unique, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -57,13 +58,31 @@ _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
return 0;
}
-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;
+static int validate_request_name_parameters(
+ sd_bus *bus,
+ const char *name,
+ uint64_t flags,
+ uint32_t *ret_param) {
+
+ uint32_t param = 0;
assert(bus);
assert(name);
+ assert(ret_param);
+
+ 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 (flags & SD_BUS_NAME_ALLOW_REPLACEMENT)
param |= BUS_NAME_ALLOW_REPLACEMENT;
@@ -72,6 +91,29 @@ static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags)
if (!(flags & SD_BUS_NAME_QUEUE))
param |= BUS_NAME_DO_NOT_QUEUE;
+ *ret_param = param;
+
+ return 0;
+}
+
+_public_ int sd_bus_request_name(
+ 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_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(name, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = validate_request_name_parameters(bus, name, flags, &param);
+ if (r < 0)
+ return r;
+
r = sd_bus_call_method(
bus,
"org.freedesktop.DBus",
@@ -90,46 +132,145 @@ static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags)
if (r < 0)
return r;
- if (ret == BUS_NAME_ALREADY_OWNER)
+ switch (ret) {
+
+ case BUS_NAME_ALREADY_OWNER:
return -EALREADY;
- else if (ret == BUS_NAME_EXISTS)
+
+ case BUS_NAME_EXISTS:
return -EEXIST;
- else if (ret == BUS_NAME_IN_QUEUE)
+
+ case BUS_NAME_IN_QUEUE:
return 0;
- else if (ret == BUS_NAME_PRIMARY_OWNER)
+
+ case BUS_NAME_PRIMARY_OWNER:
return 1;
+ }
return -EIO;
}
-_public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) {
+static int default_request_name_handler(
+ sd_bus_message *m,
+ void *userdata,
+ sd_bus_error *ret_error) {
+
+ uint32_t ret;
+ int r;
+
+ assert(m);
+
+ if (sd_bus_message_is_method_error(m, NULL)) {
+ log_debug_errno(sd_bus_message_get_errno(m),
+ "Unable to request name, failing connection: %s",
+ sd_bus_message_get_error(m)->message);
+
+ bus_enter_closing(sd_bus_message_get_bus(m));
+ return 1;
+ }
+
+ r = sd_bus_message_read(m, "u", &ret);
+ if (r < 0)
+ return r;
+
+ switch (ret) {
+
+ case BUS_NAME_ALREADY_OWNER:
+ log_debug("Already owner of requested service name, ignoring.");
+ return 1;
+
+ case BUS_NAME_IN_QUEUE:
+ log_debug("In queue for requested service name.");
+ return 1;
+
+ case BUS_NAME_PRIMARY_OWNER:
+ log_debug("Successfully acquired requested service name.");
+ return 1;
+
+ case BUS_NAME_EXISTS:
+ log_debug("Requested service name already owned, failing connection.");
+ bus_enter_closing(sd_bus_message_get_bus(m));
+ return 1;
+ }
+
+ log_debug("Unexpected response from RequestName(), failing connection.");
+ bus_enter_closing(sd_bus_message_get_bus(m));
+ return 1;
+}
+
+_public_ int sd_bus_request_name_async(
+ sd_bus *bus,
+ sd_bus_slot **ret_slot,
+ const char *name,
+ uint64_t flags,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ uint32_t param = 0;
+ int r;
+
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
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);
+
+ r = validate_request_name_parameters(bus, name, flags, &param);
+ if (r < 0)
+ return r;
+
+ return sd_bus_call_method_async(
+ bus,
+ ret_slot,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "RequestName",
+ callback ?: default_request_name_handler,
+ userdata,
+ "su",
+ name,
+ param);
+}
+
+static int validate_release_name_parameters(
+ sd_bus *bus,
+ const char *name) {
+
+ assert(bus);
+ assert(name);
+
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 */
+ /* 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;
- return bus_request_name_dbus1(bus, name, flags);
+ return 0;
}
-static int bus_release_name_dbus1(sd_bus *bus, const char *name) {
+_public_ int sd_bus_release_name(
+ 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);
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(name, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = validate_release_name_parameters(bus, name);
+ if (r < 0)
+ return r;
r = sd_bus_call_method(
bus,
@@ -147,41 +288,112 @@ static int bus_release_name_dbus1(sd_bus *bus, const char *name) {
r = sd_bus_message_read(reply, "u", &ret);
if (r < 0)
return r;
- if (ret == BUS_NAME_NON_EXISTENT)
+
+ switch (ret) {
+
+ case BUS_NAME_NON_EXISTENT:
return -ESRCH;
- if (ret == BUS_NAME_NOT_OWNER)
+
+ case BUS_NAME_NOT_OWNER:
return -EADDRINUSE;
- if (ret == BUS_NAME_RELEASED)
+
+ case BUS_NAME_RELEASED:
return 0;
+ }
+
+ return -EIO;
+}
+
+static int default_release_name_handler(
+ sd_bus_message *m,
+ void *userdata,
+ sd_bus_error *ret_error) {
+
+ uint32_t ret;
+ int r;
+
+ assert(m);
+
+ if (sd_bus_message_is_method_error(m, NULL)) {
+ log_debug_errno(sd_bus_message_get_errno(m),
+ "Unable to release name, failing connection: %s",
+ sd_bus_message_get_error(m)->message);
+
+ bus_enter_closing(sd_bus_message_get_bus(m));
+ return 1;
+ }
- return -EINVAL;
+ r = sd_bus_message_read(m, "u", &ret);
+ if (r < 0)
+ return r;
+
+ switch (ret) {
+
+ case BUS_NAME_NON_EXISTENT:
+ log_debug("Name asked to release is not taken currently, ignoring.");
+ return 1;
+
+ case BUS_NAME_NOT_OWNER:
+ log_debug("Name asked to release is owned by somebody else, ignoring.");
+ return 1;
+
+ case BUS_NAME_RELEASED:
+ log_debug("Name successfully released.");
+ return 1;
+ }
+
+ log_debug("Unexpected response from ReleaseName(), failing connection.");
+ bus_enter_closing(sd_bus_message_get_bus(m));
+ return 1;
}
-_public_ int sd_bus_release_name(sd_bus *bus, const char *name) {
+_public_ int sd_bus_release_name_async(
+ sd_bus *bus,
+ sd_bus_slot **ret_slot,
+ const char *name,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ int r;
+
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
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;
+ r = validate_release_name_parameters(bus, name);
+ if (r < 0)
+ return r;
- return bus_release_name_dbus1(bus, name);
+ return sd_bus_call_method_async(
+ bus,
+ ret_slot,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "ReleaseName",
+ callback ?: default_release_name_handler,
+ userdata,
+ "s",
+ name);
}
-static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatable) {
+_public_ int sd_bus_list_names(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;
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ 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 (acquired) {
r = sd_bus_call_method(
bus,
@@ -231,21 +443,7 @@ static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatab
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;
-
- return bus_list_names_dbus1(bus, acquired, activatable);
-}
-
-static int bus_get_name_creds_dbus1(
+_public_ int sd_bus_get_name_creds(
sd_bus *bus,
const char *name,
uint64_t mask,
@@ -257,6 +455,31 @@ static int bus_get_name_creds_dbus1(
pid_t pid = 0;
int r;
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ 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;
+
+ /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
+ * going to match. */
+ if (!bus->is_local)
+ mask &= ~SD_BUS_CREDS_AUGMENT;
+
+ if (streq(name, "org.freedesktop.DBus.Local"))
+ return -EINVAL;
+
+ if (streq(name, "org.freedesktop.DBus"))
+ return sd_bus_get_owner_creds(bus, mask, creds);
+
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
+
/* 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) {
@@ -519,51 +742,29 @@ static int bus_get_name_creds_dbus1(
return 0;
}
-_public_ int sd_bus_get_name_creds(
- sd_bus *bus,
- const char *name,
- uint64_t mask,
- sd_bus_creds **creds) {
+_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
+ bool do_label, do_groups;
+ pid_t pid = 0;
+ int r;
assert_return(bus, -EINVAL);
- assert_return(name, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
- assert_return(mask == 0 || creds, -EINVAL);
+ assert_return(ret, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(service_name_is_valid(name), -EINVAL);
-
- if (!bus->bus_client)
- return -EINVAL;
-
- /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
- * going to match. */
- if (!bus->is_local)
- mask &= ~SD_BUS_CREDS_AUGMENT;
-
- if (streq(name, "org.freedesktop.DBus.Local"))
- return -EINVAL;
-
- if (streq(name, "org.freedesktop.DBus"))
- return sd_bus_get_owner_creds(bus, mask, creds);
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
- return bus_get_name_creds_dbus1(bus, name, mask, creds);
-}
-
-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);
+ if (!bus->is_local)
+ mask &= ~SD_BUS_CREDS_AUGMENT;
do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT);
+ do_groups = bus->n_groups != (size_t) -1 && (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS);
/* Avoid allocating anything if we have no chance of returning useful data */
- if (!bus->ucred_valid && !do_label)
+ if (!bus->ucred_valid && !do_label && !do_groups)
return -ENODATA;
c = bus_creds_new();
@@ -571,17 +772,17 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **
return -ENOMEM;
if (bus->ucred_valid) {
- if (bus->ucred.pid > 0) {
+ if (pid_is_valid(bus->ucred.pid)) {
pid = c->pid = bus->ucred.pid;
c->mask |= SD_BUS_CREDS_PID & mask;
}
- if (bus->ucred.uid != UID_INVALID) {
+ if (uid_is_valid(bus->ucred.uid)) {
c->euid = bus->ucred.uid;
c->mask |= SD_BUS_CREDS_EUID & mask;
}
- if (bus->ucred.gid != GID_INVALID) {
+ if (gid_is_valid(bus->ucred.gid)) {
c->egid = bus->ucred.gid;
c->mask |= SD_BUS_CREDS_EGID & mask;
}
@@ -595,6 +796,16 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **
c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
}
+ if (do_groups) {
+ c->supplementary_gids = newdup(gid_t, bus->groups, bus->n_groups);
+ if (!c->supplementary_gids)
+ return -ENOMEM;
+
+ c->n_supplementary_gids = bus->n_groups;
+
+ c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
+ }
+
r = bus_creds_add_more(c, mask, pid, 0);
if (r < 0)
return r;
@@ -604,36 +815,23 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **
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_local)
- mask &= ~SD_BUS_CREDS_AUGMENT;
-
- return bus_get_owner_creds_dbus1(bus, mask, ret);
-}
-
-#define internal_match(bus, m) \
- ((bus)->hello_flags & KDBUS_HELLO_MONITOR \
+#define append_eavesdrop(bus, m) \
+ ((bus)->is_monitor \
? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
: (m))
-static int bus_add_match_internal_dbus1(
+int bus_add_match_internal(
sd_bus *bus,
const char *match) {
const char *e;
assert(bus);
- assert(match);
- e = internal_match(bus, match);
+ if (!bus->bus_client)
+ return -EINVAL;
+
+ e = append_eavesdrop(bus, match);
return sd_bus_call_method(
bus,
@@ -646,22 +844,36 @@ static int bus_add_match_internal_dbus1(
"s",
e);
}
-
-int bus_add_match_internal(
+int bus_add_match_internal_async(
sd_bus *bus,
+ sd_bus_slot **ret_slot,
const char *match,
- struct bus_match_component *components,
- unsigned n_components) {
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ const char *e;
assert(bus);
if (!bus->bus_client)
return -EINVAL;
- return bus_add_match_internal_dbus1(bus, match);
+ e = append_eavesdrop(bus, match);
+
+ return sd_bus_call_method_async(
+ bus,
+ ret_slot,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "AddMatch",
+ callback,
+ userdata,
+ "s",
+ e);
}
-static int bus_remove_match_internal_dbus1(
+int bus_remove_match_internal(
sd_bus *bus,
const char *match) {
@@ -670,10 +882,16 @@ static int bus_remove_match_internal_dbus1(
assert(bus);
assert(match);
- e = internal_match(bus, match);
+ if (!bus->bus_client)
+ return -EINVAL;
- return sd_bus_call_method(
+ e = append_eavesdrop(bus, match);
+
+ /* Fire and forget */
+
+ return sd_bus_call_method_async(
bus,
+ NULL,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
@@ -684,24 +902,13 @@ static int bus_remove_match_internal_dbus1(
e);
}
-int bus_remove_match_internal(
- sd_bus *bus,
- const char *match) {
-
- assert(bus);
-
- if (!bus->bus_client)
- return -EINVAL;
-
- 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(bus = bus_resolve(bus), -ENOPKG);
assert_return(name, -EINVAL);
assert_return(machine, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
diff --git a/src/libsystemd/sd-bus/bus-control.h b/src/libsystemd/sd-bus/bus-control.h
index c9d434c607..3d9acebaf6 100644
--- a/src/libsystemd/sd-bus/bus-control.h
+++ b/src/libsystemd/sd-bus/bus-control.h
@@ -22,7 +22,7 @@
#include "sd-bus.h"
-#include "bus-match.h"
+int bus_add_match_internal(sd_bus *bus, const char *match);
+int bus_add_match_internal_async(sd_bus *bus, sd_bus_slot **ret, const char *match, sd_bus_message_handler_t callback, void *userdata);
-int bus_add_match_internal(sd_bus *bus, const char *match, struct bus_match_component *components, unsigned n_components);
int bus_remove_match_internal(sd_bus *bus, const char *match);
diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c
index 9d3b596429..8da6640ca0 100644
--- a/src/libsystemd/sd-bus/bus-convenience.c
+++ b/src/libsystemd/sd-bus/bus-convenience.c
@@ -36,6 +36,7 @@ _public_ int sd_bus_emit_signal(
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
if (!BUS_IS_OPEN(bus->state))
@@ -73,6 +74,7 @@ _public_ int sd_bus_call_method_async(
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
if (!BUS_IS_OPEN(bus->state))
@@ -615,3 +617,71 @@ _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability)
return 0;
}
+
+#define make_expression(sender, path, interface, member) \
+ strjoina( \
+ "type='signal'", \
+ sender ? ",sender='" : "", \
+ sender ?: "", \
+ sender ? "'" : "", \
+ path ? ",path='" : "", \
+ path ?: "", \
+ path ? "'" : "", \
+ interface ? ",interface='" : "", \
+ interface ?: "", \
+ interface ? "'" : "", \
+ member ? ",member='" : "", \
+ member ?: "", \
+ member ? "'" : "" \
+ )
+
+_public_ int sd_bus_match_signal(
+ sd_bus *bus,
+ sd_bus_slot **ret,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ const char *expression;
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(!sender || service_name_is_valid(sender), -EINVAL);
+ assert_return(!path || object_path_is_valid(path), -EINVAL);
+ assert_return(!interface || interface_name_is_valid(interface), -EINVAL);
+ assert_return(!member || member_name_is_valid(member), -EINVAL);
+
+ expression = make_expression(sender, path, interface, member);
+
+ return sd_bus_add_match(bus, ret, expression, callback, userdata);
+}
+
+_public_ int sd_bus_match_signal_async(
+ sd_bus *bus,
+ sd_bus_slot **ret,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_message_handler_t callback,
+ sd_bus_message_handler_t install_callback,
+ void *userdata) {
+
+ const char *expression;
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(!sender || service_name_is_valid(sender), -EINVAL);
+ assert_return(!path || object_path_is_valid(path), -EINVAL);
+ assert_return(!interface || interface_name_is_valid(interface), -EINVAL);
+ assert_return(!member || member_name_is_valid(member), -EINVAL);
+
+ expression = make_expression(sender, path, interface, member);
+
+ return sd_bus_add_match_async(bus, ret, expression, callback, install_callback, userdata);
+}
diff --git a/src/libsystemd/sd-bus/bus-gvariant.c b/src/libsystemd/sd-bus/bus-gvariant.c
index 6a990a02c0..e6ab984d1f 100644
--- a/src/libsystemd/sd-bus/bus-gvariant.c
+++ b/src/libsystemd/sd-bus/bus-gvariant.c
@@ -18,6 +18,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <string.h>
+
+#include "sd-bus.h"
+
#include "bus-gvariant.h"
#include "bus-signature.h"
#include "bus-type.h"
diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c
index 3c381b0ffe..05a022fbf3 100644
--- a/src/libsystemd/sd-bus/bus-internal.c
+++ b/src/libsystemd/sd-bus/bus-internal.c
@@ -362,13 +362,18 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
} else
return r;
- log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s",
+ log_debug("Failed to process message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s: %s",
bus_message_type_to_string(m->header->type),
- strna(m->sender),
- strna(m->path),
- strna(m->interface),
- strna(m->member),
+ 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->root_container.signature),
+ strna(m->error.name),
+ strna(m->error.message),
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
index 378c408ea3..1b55cdafed 100644
--- a/src/libsystemd/sd-bus/bus-internal.h
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -38,7 +38,7 @@
struct reply_callback {
sd_bus_message_handler_t callback;
- usec_t timeout;
+ usec_t timeout_usec; /* this is a relative timeout until we reach the BUS_HELLO state, and an absolute one right after */
uint64_t cookie;
unsigned prioq_idx;
};
@@ -53,6 +53,9 @@ struct filter_callback {
struct match_callback {
sd_bus_message_handler_t callback;
+ sd_bus_message_handler_t install_callback;
+
+ sd_bus_slot *install_slot; /* The AddMatch() call */
unsigned last_iteration;
@@ -157,12 +160,14 @@ struct sd_bus_slot {
enum bus_state {
BUS_UNSET,
- BUS_OPENING,
- BUS_AUTHENTICATING,
- BUS_HELLO,
+ BUS_WATCH_BIND, /* waiting for the socket to appear via inotify */
+ BUS_OPENING, /* the kernel's connect() is still not ready */
+ BUS_AUTHENTICATING, /* we are currently in the "SASL" authorization phase of dbus */
+ BUS_HELLO, /* we are waiting for the Hello() response */
BUS_RUNNING,
BUS_CLOSING,
- BUS_CLOSED
+ BUS_CLOSED,
+ _BUS_STATE_MAX,
};
static inline bool BUS_IS_OPEN(enum bus_state state) {
@@ -188,6 +193,7 @@ struct sd_bus {
enum bus_state state;
int input_fd, output_fd;
+ int inotify_fd;
int message_version;
int message_endian;
@@ -210,6 +216,11 @@ struct sd_bus {
bool exited:1;
bool exit_triggered:1;
bool is_local:1;
+ bool watch_bind:1;
+ bool is_monitor:1;
+ bool accept_fd:1;
+ bool attach_timestamp:1;
+ bool connected_signal:1;
int use_memfd;
@@ -261,6 +272,8 @@ struct sd_bus {
struct ucred ucred;
char *label;
+ gid_t *groups;
+ size_t n_groups;
uint64_t creds_mask;
@@ -284,13 +297,11 @@ struct sd_bus {
pid_t original_pid;
- uint64_t hello_flags;
- uint64_t attach_flags;
-
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_source *inotify_event_source;
sd_event *event;
int event_priority;
@@ -305,11 +316,15 @@ struct sd_bus {
char *cgroup_root;
char *description;
+ char *patch_sender;
sd_bus_track *track_queue;
LIST_HEAD(sd_bus_slot, slots);
LIST_HEAD(sd_bus_track, tracks);
+
+ int *inotify_watches;
+ size_t n_inotify_watches;
};
/* For method calls we time-out at 25s, like in the D-Bus reference implementation */
@@ -353,6 +368,8 @@ const char *bus_message_type_to_string(uint8_t u) _pure_;
#define error_name_is_valid interface_name_is_valid
+sd_bus *bus_resolve(sd_bus *bus);
+
int bus_ensure_running(sd_bus *bus);
int bus_start_running(sd_bus *bus);
int bus_next_address(sd_bus *bus);
@@ -365,6 +382,12 @@ bool bus_pid_changed(sd_bus *bus);
char *bus_address_escape(const char *v);
+int bus_attach_io_events(sd_bus *b);
+int bus_attach_inotify_event(sd_bus *b);
+
+void bus_close_inotify_fd(sd_bus *b);
+void bus_close_io_fds(sd_bus *b);
+
#define OBJECT_PATH_FOREACH_PREFIX(prefix, path) \
for (char *_slash = ({ strcpy((prefix), (path)); streq((prefix), "/") ? NULL : strrchr((prefix), '/'); }) ; \
_slash && !(_slash[(_slash) == (prefix)] = 0); \
@@ -381,8 +404,6 @@ 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);
@@ -393,64 +414,6 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);
return sd_bus_error_set_errno(error, r); \
} while (false)
-/**
- * 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
-};
+void bus_enter_closing(sd_bus *bus);
-/**
- * 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,
-};
+void bus_set_state(sd_bus *bus, enum bus_state state);
diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c
index c6179b4d95..b27b9d7d86 100644
--- a/src/libsystemd/sd-bus/bus-kernel.c
+++ b/src/libsystemd/sd-bus/bus-kernel.c
@@ -66,49 +66,3 @@ void bus_flush_memfd(sd_bus *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 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;
-}
diff --git a/src/libsystemd/sd-bus/bus-kernel.h b/src/libsystemd/sd-bus/bus-kernel.h
index d9f80935fe..fa78e5c80d 100644
--- a/src/libsystemd/sd-bus/bus-kernel.h
+++ b/src/libsystemd/sd-bus/bus-kernel.h
@@ -41,5 +41,3 @@ struct memfd_cache {
void close_and_munmap(int fd, void *address, size_t size);
void bus_flush_memfd(sd_bus *bus);
-
-uint64_t attach_flags_to_kdbus(uint64_t sd_bus_flags);
diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c
index 219cff1f6e..95a87da08b 100644
--- a/src/libsystemd/sd-bus/bus-message.c
+++ b/src/libsystemd/sd-bus/bus-message.c
@@ -127,7 +127,6 @@ static void message_free(sd_bus_message *m) {
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);
@@ -1321,7 +1320,9 @@ static void *message_extend_body(
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 */
+ (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) {
@@ -1368,7 +1369,7 @@ static void *message_extend_body(
}
} else
/* Return something that is not NULL and is aligned */
- p = (uint8_t *) NULL + align;
+ p = (uint8_t*) align;
m->body_size = end_body;
message_extend_containers(m, added);
@@ -4779,7 +4780,7 @@ _public_ int sd_bus_message_read_array(
if (sz == 0)
/* Zero length array, let's return some aligned
* pointer that is not NULL */
- p = (uint8_t*) NULL + align;
+ p = (uint8_t*) align;
else {
r = message_peek_body(m, &m->rindex, align, sz, &p);
if (r < 0)
@@ -5488,6 +5489,15 @@ _public_ int sd_bus_message_set_destination(sd_bus_message *m, const char *desti
return message_append_field_string(m, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &m->destination);
}
+_public_ int sd_bus_message_set_sender(sd_bus_message *m, const char *sender) {
+ assert_return(m, -EINVAL);
+ assert_return(sender, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(!m->sender, -EEXIST);
+
+ return message_append_field_string(m, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, sender, &m->sender);
+}
+
int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) {
size_t total;
void *p, *e;
diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h
index 1e4b20926d..88998700d6 100644
--- a/src/libsystemd/sd-bus/bus-message.h
+++ b/src/libsystemd/sd-bus/bus-message.h
@@ -136,10 +136,6 @@ struct sd_bus_message {
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;
};
diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c
index 121197bbcb..6e00255b20 100644
--- a/src/libsystemd/sd-bus/bus-objects.c
+++ b/src/libsystemd/sd-bus/bus-objects.c
@@ -1369,7 +1369,7 @@ int bus_process_object(sd_bus *bus, sd_bus_message *m) {
assert(bus);
assert(m);
- if (bus->hello_flags & KDBUS_HELLO_MONITOR)
+ if (bus->is_monitor)
return 0;
if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
@@ -1547,6 +1547,7 @@ static int bus_add_object(
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(callback, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -1650,6 +1651,7 @@ static int add_object_vtable_internal(
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(interface_name_is_valid(interface), -EINVAL);
assert_return(vtable, -EINVAL);
@@ -1859,6 +1861,7 @@ _public_ int sd_bus_add_node_enumerator(
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(callback, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2110,6 +2113,7 @@ _public_ int sd_bus_emit_properties_changed_strv(
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(interface_name_is_valid(interface), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2156,6 +2160,7 @@ _public_ int sd_bus_emit_properties_changed(
char **names;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(interface_name_is_valid(interface), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2340,6 +2345,7 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
*/
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2510,6 +2516,7 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
*/
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2663,6 +2670,7 @@ _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, ch
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2729,6 +2737,7 @@ _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const c
char **interfaces;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2746,6 +2755,7 @@ _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path,
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2780,6 +2790,7 @@ _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const
char **interfaces;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2797,6 +2808,7 @@ _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const ch
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
diff --git a/src/libsystemd/sd-bus/bus-signature.c b/src/libsystemd/sd-bus/bus-signature.c
index d16461f4ae..f3cd9bd0fa 100644
--- a/src/libsystemd/sd-bus/bus-signature.c
+++ b/src/libsystemd/sd-bus/bus-signature.c
@@ -20,6 +20,8 @@
#include <util.h>
+#include "sd-bus.h"
+
#include "bus-signature.h"
#include "bus-type.h"
diff --git a/src/libsystemd/sd-bus/bus-slot.c b/src/libsystemd/sd-bus/bus-slot.c
index 756761c3ed..9a56371715 100644
--- a/src/libsystemd/sd-bus/bus-slot.c
+++ b/src/libsystemd/sd-bus/bus-slot.c
@@ -81,7 +81,7 @@ void bus_slot_disconnect(sd_bus_slot *slot) {
if (slot->reply_callback.cookie != 0)
ordered_hashmap_remove(slot->bus->reply_callbacks, &slot->reply_callback.cookie);
- if (slot->reply_callback.timeout != 0)
+ if (slot->reply_callback.timeout_usec != 0)
prioq_remove(slot->bus->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx);
break;
@@ -94,12 +94,17 @@ void bus_slot_disconnect(sd_bus_slot *slot) {
case BUS_MATCH_CALLBACK:
if (slot->match_added)
- bus_remove_match_internal(slot->bus, slot->match_callback.match_string);
+ (void) bus_remove_match_internal(slot->bus, slot->match_callback.match_string);
+
+ if (slot->match_callback.install_slot) {
+ bus_slot_disconnect(slot->match_callback.install_slot);
+ slot->match_callback.install_slot = sd_bus_slot_unref(slot->match_callback.install_slot);
+ }
slot->bus->match_callbacks_modified = true;
bus_match_remove(&slot->bus->match_callbacks, &slot->match_callback);
- free(slot->match_callback.match_string);
+ slot->match_callback.match_string = mfree(slot->match_callback.match_string);
break;
@@ -174,7 +179,7 @@ void bus_slot_disconnect(sd_bus_slot *slot) {
}
}
- free(slot->node_vtable.interface);
+ slot->node_vtable.interface = mfree(slot->node_vtable.interface);
if (slot->node_vtable.node) {
LIST_REMOVE(vtables, slot->node_vtable.node->vtables, &slot->node_vtable);
diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
index 07a9c8affd..2fe86b61c4 100644
--- a/src/libsystemd/sd-bus/bus-socket.c
+++ b/src/libsystemd/sd-bus/bus-socket.c
@@ -32,9 +32,13 @@
#include "bus-socket.h"
#include "fd-util.h"
#include "format-util.h"
+#include "fs-util.h"
#include "hexdecoct.h"
+#include "io-util.h"
#include "macro.h"
#include "missing.h"
+#include "path-util.h"
+#include "process-util.h"
#include "selinux-util.h"
#include "signal-util.h"
#include "stdio-util.h"
@@ -188,7 +192,7 @@ static int bus_socket_auth_verify_client(sd_bus *b) {
if (!e)
return 0;
- if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) {
+ if (b->accept_fd) {
f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
if (!f)
return 0;
@@ -475,7 +479,7 @@ static int bus_socket_auth_verify_server(sd_bus *b) {
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))
+ if (b->auth == _BUS_AUTH_INVALID || !b->accept_fd)
r = bus_socket_auth_write(b, "ERROR\r\n");
else {
b->can_fds = true;
@@ -592,8 +596,8 @@ 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);
+ (void) fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE);
+ (void) fd_inc_sndbuf(b->output_fd, SNDBUF_SIZE);
b->message_version = 1;
b->message_endian = 0;
@@ -603,16 +607,24 @@ static void bus_get_peercred(sd_bus *b) {
int r;
assert(b);
+ assert(!b->ucred_valid);
+ assert(!b->label);
+ assert(b->n_groups == (size_t) -1);
/* 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_use()) {
- r = getpeersec(b->input_fd, &b->label);
- if (r < 0 && r != -EOPNOTSUPP)
- log_debug_errno(r, "Failed to determine peer security context: %m");
- }
+ r = getpeersec(b->input_fd, &b->label);
+ if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT))
+ log_debug_errno(r, "Failed to determine peer security context: %m");
+
+ /* Get the list of auxiliary groups of the peer */
+ r = getpeergroups(b->input_fd, &b->groups);
+ if (r >= 0)
+ b->n_groups = (size_t) r;
+ else if (!IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT))
+ log_debug_errno(r, "Failed to determine peer's group list: %m");
}
static int bus_socket_start_auth_client(sd_bus *b) {
@@ -641,7 +653,7 @@ static int bus_socket_start_auth_client(sd_bus *b) {
if (!b->auth_buffer)
return -ENOMEM;
- if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD)
+ if (b->accept_fd)
auth_suffix = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n";
else
auth_suffix = "\r\nBEGIN\r\n";
@@ -661,15 +673,15 @@ int bus_socket_start_auth(sd_bus *b) {
bus_get_peercred(b);
- b->state = BUS_AUTHENTICATING;
+ bus_set_state(b, BUS_AUTHENTICATING);
b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_AUTH_TIMEOUT;
if (sd_is_socket(b->input_fd, AF_UNIX, 0, 0) <= 0)
- b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD;
+ b->accept_fd = false;
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;
+ b->accept_fd = false;
if (b->is_server)
return bus_socket_read_auth(b);
@@ -677,30 +689,249 @@ int bus_socket_start_auth(sd_bus *b) {
return bus_socket_start_auth_client(b);
}
+static int bus_socket_inotify_setup(sd_bus *b) {
+ _cleanup_free_ int *new_watches = NULL;
+ _cleanup_free_ char *absolute = NULL;
+ size_t n_allocated = 0, n = 0, done = 0, i;
+ unsigned max_follow = 32;
+ const char *p;
+ int wd, r;
+
+ assert(b);
+ assert(b->watch_bind);
+ assert(b->sockaddr.sa.sa_family == AF_UNIX);
+ assert(b->sockaddr.un.sun_path[0] != 0);
+
+ /* Sets up an inotify fd in case watch_bind is enabled: wait until the configured AF_UNIX file system socket
+ * appears before connecting to it. The implemented is pretty simplistic: we just subscribe to relevant changes
+ * to all prefix components of the path, and every time we get an event for that we try to reconnect again,
+ * without actually caring what precisely the event we got told us. If we still can't connect we re-subscribe
+ * to all relevant changes of anything in the path, so that our watches include any possibly newly created path
+ * components. */
+
+ if (b->inotify_fd < 0) {
+ b->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+ if (b->inotify_fd < 0)
+ return -errno;
+ }
+
+ /* Make sure the path is NUL terminated */
+ p = strndupa(b->sockaddr.un.sun_path, sizeof(b->sockaddr.un.sun_path));
+
+ /* Make sure the path is absolute */
+ r = path_make_absolute_cwd(p, &absolute);
+ if (r < 0)
+ goto fail;
+
+ /* Watch all parent directories, and don't mind any prefix that doesn't exist yet. For the innermost directory
+ * that exists we want to know when files are created or moved into it. For all parents of it we just care if
+ * they are removed or renamed. */
+
+ if (!GREEDY_REALLOC(new_watches, n_allocated, n + 1)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ /* Start with the top-level directory, which is a bit simpler than the rest, since it can't be a symlink, and
+ * always exists */
+ wd = inotify_add_watch(b->inotify_fd, "/", IN_CREATE|IN_MOVED_TO);
+ if (wd < 0) {
+ r = log_debug_errno(errno, "Failed to add inotify watch on /: %m");
+ goto fail;
+ } else
+ new_watches[n++] = wd;
+
+ for (;;) {
+ _cleanup_free_ char *component = NULL, *prefix = NULL, *destination = NULL;
+ size_t n_slashes, n_component;
+ char *c = NULL;
+
+ n_slashes = strspn(absolute + done, "/");
+ n_component = n_slashes + strcspn(absolute + done + n_slashes, "/");
+
+ if (n_component == 0) /* The end */
+ break;
+
+ component = strndup(absolute + done, n_component);
+ if (!component) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ /* A trailing slash? That's a directory, and not a socket then */
+ if (path_equal(component, "/")) {
+ r = -EISDIR;
+ goto fail;
+ }
+
+ /* A single dot? Let's eat this up */
+ if (path_equal(component, "/.")) {
+ done += n_component;
+ continue;
+ }
+
+ prefix = strndup(absolute, done + n_component);
+ if (!prefix) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!GREEDY_REALLOC(new_watches, n_allocated, n + 1)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ wd = inotify_add_watch(b->inotify_fd, prefix, IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO|IN_DONT_FOLLOW);
+ log_debug("Added inotify watch for %s on bus %s: %i", prefix, strna(b->description), wd);
+
+ if (wd < 0) {
+ if (IN_SET(errno, ENOENT, ELOOP))
+ break; /* This component doesn't exist yet, or the path contains a cyclic symlink right now */
+
+ r = log_debug_errno(errno, "Failed to add inotify watch on %s: %m", isempty(prefix) ? "/" : prefix);
+ goto fail;
+ } else
+ new_watches[n++] = wd;
+
+ /* Check if this is possibly a symlink. If so, let's follow it and watch it too. */
+ r = readlink_malloc(prefix, &destination);
+ if (r == -EINVAL) { /* not a symlink */
+ done += n_component;
+ continue;
+ }
+ if (r < 0)
+ goto fail;
+
+ if (isempty(destination)) { /* Empty symlink target? Yuck! */
+ r = -EINVAL;
+ goto fail;
+ }
+
+ if (max_follow <= 0) { /* Let's make sure we don't follow symlinks forever */
+ r = -ELOOP;
+ goto fail;
+ }
+
+ if (path_is_absolute(destination)) {
+ /* For absolute symlinks we build the new path and start anew */
+ c = strjoin(destination, absolute + done + n_component);
+ done = 0;
+ } else {
+ _cleanup_free_ char *t = NULL;
+
+ /* For relative symlinks we replace the last component, and try again */
+ t = strndup(absolute, done);
+ if (!t)
+ return -ENOMEM;
+
+ c = strjoin(t, "/", destination, absolute + done + n_component);
+ }
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ free(absolute);
+ absolute = c;
+
+ max_follow--;
+ }
+
+ /* And now, let's remove all watches from the previous iteration we don't need anymore */
+ for (i = 0; i < b->n_inotify_watches; i++) {
+ bool found = false;
+ size_t j;
+
+ for (j = 0; j < n; j++)
+ if (new_watches[j] == b->inotify_watches[i]) {
+ found = true;
+ break;
+ }
+
+ if (found)
+ continue;
+
+ (void) inotify_rm_watch(b->inotify_fd, b->inotify_watches[i]);
+ }
+
+ free_and_replace(b->inotify_watches, new_watches);
+ b->n_inotify_watches = n;
+
+ return 0;
+
+fail:
+ bus_close_inotify_fd(b);
+ return r;
+}
+
int bus_socket_connect(sd_bus *b) {
+ bool inotify_done = false;
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;
+ for (;;) {
+ assert(b->input_fd < 0);
+ assert(b->output_fd < 0);
+ assert(b->sockaddr.sa.sa_family != AF_UNSPEC);
- b->output_fd = b->input_fd;
+ b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (b->input_fd < 0)
+ return -errno;
- bus_socket_setup(b);
+ 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;
+ if (connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size) < 0) {
+ if (errno == EINPROGRESS) {
- return -errno;
+ /* If we have any inotify watches open, close them now, we don't need them anymore, as
+ * we have successfully initiated a connection */
+ bus_close_inotify_fd(b);
+
+ /* Note that very likely we are already in BUS_OPENING state here, as we enter it when
+ * we start parsing the address string. The only reason we set the state explicitly
+ * here, is to undo BUS_WATCH_BIND, in case we did the inotify magic. */
+ bus_set_state(b, BUS_OPENING);
+ return 1;
+ }
+
+ if (IN_SET(errno, ENOENT, ECONNREFUSED) && /* ENOENT → unix socket doesn't exist at all; ECONNREFUSED → unix socket stale */
+ b->watch_bind &&
+ b->sockaddr.sa.sa_family == AF_UNIX &&
+ b->sockaddr.un.sun_path[0] != 0) {
+
+ /* This connection attempt failed, let's release the socket for now, and start with a
+ * fresh one when reconnecting. */
+ bus_close_io_fds(b);
+
+ if (inotify_done) {
+ /* inotify set up already, don't do it again, just return now, and remember
+ * that we are waiting for inotify events now. */
+ bus_set_state(b, BUS_WATCH_BIND);
+ return 1;
+ }
+
+ /* This is a file system socket, and the inotify logic is enabled. Let's create the necessary inotify fd. */
+ r = bus_socket_inotify_setup(b);
+ if (r < 0)
+ return r;
+
+ /* Let's now try to connect a second time, because in theory there's otherwise a race
+ * here: the socket might have been created in the time between our first connect() and
+ * the time we set up the inotify logic. But let's remember that we set up inotify now,
+ * so that we don't do the connect() more than twice. */
+ inotify_done = true;
+
+ } else
+ return -errno;
+ } else
+ break;
}
+ /* Yay, established, we don't need no inotify anymore! */
+ bus_close_inotify_fd(b);
+
return bus_socket_start_auth(b);
}
@@ -717,29 +948,24 @@ int bus_socket_exec(sd_bus *b) {
if (r < 0)
return -errno;
- pid = fork();
- if (pid < 0) {
+ r = safe_fork_full("(sd-busexec)", s+1, 1, FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, &pid);
+ if (r < 0) {
safe_close_pair(s);
- return -errno;
+ return r;
}
- if (pid == 0) {
+ if (r == 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 (!IN_SET(s[1], STDIN_FILENO, 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);
+ (void) fd_cloexec(STDIN_FILENO, false);
+ (void) fd_cloexec(STDOUT_FILENO, false);
+ (void) fd_nonblock(STDIN_FILENO, false);
+ (void) fd_nonblock(STDOUT_FILENO, false);
if (b->exec_argv)
execvp(b->exec_path, b->exec_argv);
@@ -1063,3 +1289,34 @@ int bus_socket_process_authenticating(sd_bus *b) {
return bus_socket_read_auth(b);
}
+
+int bus_socket_process_watch_bind(sd_bus *b) {
+ int r, q;
+
+ assert(b);
+ assert(b->state == BUS_WATCH_BIND);
+ assert(b->inotify_fd >= 0);
+
+ r = flush_fd(b->inotify_fd);
+ if (r <= 0)
+ return r;
+
+ log_debug("Got inotify event on bus %s.", strna(b->description));
+
+ /* We flushed events out of the inotify fd. In that case, maybe the socket is valid now? Let's try to connect
+ * to it again */
+
+ r = bus_socket_connect(b);
+ if (r < 0)
+ return r;
+
+ q = bus_attach_io_events(b);
+ if (q < 0)
+ return q;
+
+ q = bus_attach_inotify_event(b);
+ if (q < 0)
+ return q;
+
+ return r;
+}
diff --git a/src/libsystemd/sd-bus/bus-socket.h b/src/libsystemd/sd-bus/bus-socket.h
index 915a283f5a..c180562f98 100644
--- a/src/libsystemd/sd-bus/bus-socket.h
+++ b/src/libsystemd/sd-bus/bus-socket.h
@@ -34,5 +34,6 @@ int bus_socket_read_message(sd_bus *bus);
int bus_socket_process_opening(sd_bus *b);
int bus_socket_process_authenticating(sd_bus *b);
+int bus_socket_process_watch_bind(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
index ab22d6e4de..5482d39a01 100644
--- a/src/libsystemd/sd-bus/bus-track.c
+++ b/src/libsystemd/sd-bus/bus-track.c
@@ -48,25 +48,13 @@ struct sd_bus_track {
LIST_FIELDS(sd_bus_track, tracks);
};
-#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; \
- })
+#define MATCH_FOR_NAME(name) \
+ strjoina("type='signal'," \
+ "sender='org.freedesktop.DBus'," \
+ "path='/org/freedesktop/DBus'," \
+ "interface='org.freedesktop.DBus'," \
+ "member='NameOwnerChanged'," \
+ "arg0='", name, "'")
static struct track_item* track_item_free(struct track_item *i) {
@@ -148,6 +136,7 @@ _public_ int sd_bus_track_new(
sd_bus_track *t;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(track, -EINVAL);
if (!bus->bus_client)
@@ -259,9 +248,7 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */
- track->n_adding++; /* make sure we aren't dispatched while we synchronously add this match */
- r = sd_bus_add_match(track->bus, &n->slot, match, on_name_owner_changed, track);
- track->n_adding--;
+ r = sd_bus_add_match_async(track->bus, &n->slot, match, on_name_owner_changed, NULL, track);
if (r < 0) {
bus_track_add_to_queue(track);
return r;
diff --git a/src/libsystemd/sd-bus/bus-type.c b/src/libsystemd/sd-bus/bus-type.c
index fe486f441d..980b35d8ea 100644
--- a/src/libsystemd/sd-bus/bus-type.c
+++ b/src/libsystemd/sd-bus/bus-type.c
@@ -18,6 +18,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+
+#include "sd-bus.h"
+
#include "bus-type.h"
bool bus_type_is_valid(char c) {
diff --git a/src/libsystemd/sd-bus/bus-type.h b/src/libsystemd/sd-bus/bus-type.h
index ae272b1e6a..834f09777a 100644
--- a/src/libsystemd/sd-bus/bus-type.h
+++ b/src/libsystemd/sd-bus/bus-type.h
@@ -22,8 +22,6 @@
#include <stdbool.h>
-#include "sd-bus.h"
-
#include "macro.h"
bool bus_type_is_valid(char c) _const_;
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index 5fb15a7aaf..7e7ebb27a7 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -50,6 +50,7 @@
#include "macro.h"
#include "missing.h"
#include "parse-util.h"
+#include "process-util.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
@@ -57,7 +58,7 @@
#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-name=%s error-message=%s", \
+ log_debug("Got message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s", \
bus_message_type_to_string(_mm->header->type), \
strna(sd_bus_message_get_sender(_mm)), \
strna(sd_bus_message_get_destination(_mm)), \
@@ -66,28 +67,97 @@
strna(sd_bus_message_get_member(_mm)), \
BUS_MESSAGE_COOKIE(_mm), \
_mm->reply_cookie, \
+ strna(_mm->root_container.signature), \
strna(_mm->error.name), \
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 void bus_detach_io_events(sd_bus *b);
+static void bus_detach_inotify_event(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) {
+static sd_bus **bus_choose_default(int (**bus_open)(sd_bus **)) {
+ 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")) {
+ if (bus_open)
+ *bus_open = sd_bus_open_system;
+ return &default_system_bus;
+ } else if (STR_IN_SET(e, "user", "session")) {
+ if (bus_open)
+ *bus_open = sd_bus_open_user;
+ return &default_user_bus;
+ }
+ }
+
+ /* 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) {
+ if (bus_open)
+ *bus_open = sd_bus_open;
+ return &default_starter_bus;
+ }
+
+ /* Finally, if nothing is set use the cached connection for
+ * the right scope */
+
+ if (cg_pid_get_owner_uid(0, NULL) >= 0) {
+ if (bus_open)
+ *bus_open = sd_bus_open_user;
+ return &default_user_bus;
+ } else {
+ if (bus_open)
+ *bus_open = sd_bus_open_system;
+ return &default_system_bus;
+ }
+}
+
+sd_bus *bus_resolve(sd_bus *bus) {
+ switch ((uintptr_t) bus) {
+ case (uintptr_t) SD_BUS_DEFAULT:
+ return *(bus_choose_default(NULL));
+ case (uintptr_t) SD_BUS_DEFAULT_USER:
+ return default_user_bus;
+ case (uintptr_t) SD_BUS_DEFAULT_SYSTEM:
+ return default_system_bus;
+ default:
+ return bus;
+ }
+}
+
+void bus_close_io_fds(sd_bus *b) {
assert(b);
- detach_io_events(b);
+ bus_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);
}
+void bus_close_inotify_fd(sd_bus *b) {
+ assert(b);
+
+ bus_detach_inotify_event(b);
+
+ b->inotify_fd = safe_close(b->inotify_fd);
+ b->inotify_watches = mfree(b->inotify_watches);
+ b->n_inotify_watches = 0;
+}
+
static void bus_reset_queues(sd_bus *b) {
assert(b);
@@ -131,9 +201,11 @@ static void bus_free(sd_bus *b) {
if (b->default_bus_ptr)
*b->default_bus_ptr = NULL;
- bus_close_fds(b);
+ bus_close_io_fds(b);
+ bus_close_inotify_fd(b);
free(b->label);
+ free(b->groups);
free(b->rbuffer);
free(b->unique_name);
free(b->auth_buffer);
@@ -141,6 +213,7 @@ static void bus_free(sd_bus *b) {
free(b->machine);
free(b->cgroup_root);
free(b->description);
+ free(b->patch_sender);
free(b->exec_path);
strv_free(b->exec_argv);
@@ -180,11 +253,12 @@ _public_ int sd_bus_new(sd_bus **ret) {
r->n_ref = REFCNT_INIT;
r->input_fd = r->output_fd = -1;
+ r->inotify_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->accept_fd = true;
r->original_pid = getpid_cached();
+ r->n_groups = (size_t) -1;
assert_se(pthread_mutex_init(&r->memfd_cache_mutex, NULL) == 0);
@@ -203,6 +277,7 @@ _public_ int sd_bus_set_address(sd_bus *bus, const char *address) {
char *a;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(address, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -219,6 +294,7 @@ _public_ int sd_bus_set_address(sd_bus *bus, const char *address) {
_public_ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(input_fd >= 0, -EBADF);
assert_return(output_fd >= 0, -EBADF);
@@ -233,6 +309,7 @@ _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 = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(path, -EINVAL);
assert_return(!strv_isempty(argv), -EINVAL);
@@ -259,7 +336,9 @@ _public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[])
_public_ int sd_bus_set_bus_client(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus->patch_sender, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
bus->bus_client = !!b;
@@ -268,43 +347,40 @@ _public_ int sd_bus_set_bus_client(sd_bus *bus, int b) {
_public_ int sd_bus_set_monitor(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
- SET_FLAG(bus->hello_flags, KDBUS_HELLO_MONITOR, b);
+ bus->is_monitor = b;
return 0;
}
_public_ int sd_bus_negotiate_fds(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
- SET_FLAG(bus->hello_flags, KDBUS_HELLO_ACCEPT_FD, b);
+ bus->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(bus = bus_resolve(bus), -ENOPKG);
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;
+ /* This is not actually supported by any of our transports these days, but we do honour it for synthetic
+ * replies, and maybe one day classic D-Bus learns this too */
+ bus->attach_timestamp = b;
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(bus = bus_resolve(bus), -ENOPKG);
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);
@@ -314,18 +390,12 @@ _public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) {
/* 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;
-
return 0;
}
_public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
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);
@@ -337,6 +407,7 @@ _public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) {
_public_ int sd_bus_set_anonymous(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -346,6 +417,7 @@ _public_ int sd_bus_set_anonymous(sd_bus *bus, int b) {
_public_ int sd_bus_set_trusted(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -355,6 +427,7 @@ _public_ int sd_bus_set_trusted(sd_bus *bus, int b) {
_public_ int sd_bus_set_description(sd_bus *bus, const char *description) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -363,6 +436,7 @@ _public_ int sd_bus_set_description(sd_bus *bus, const char *description) {
_public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
bus->allow_interactive_authorization = !!b;
@@ -371,11 +445,115 @@ _public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) {
_public_ int sd_bus_get_allow_interactive_authorization(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->allow_interactive_authorization;
}
+_public_ int sd_bus_set_watch_bind(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ bus->watch_bind = b;
+ return 0;
+}
+
+_public_ int sd_bus_get_watch_bind(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ return bus->watch_bind;
+}
+
+_public_ int sd_bus_set_connected_signal(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ bus->connected_signal = b;
+ return 0;
+}
+
+_public_ int sd_bus_get_connected_signal(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ return bus->connected_signal;
+}
+
+static int synthesize_connected_signal(sd_bus *bus) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ int r;
+
+ assert(bus);
+
+ /* If enabled, synthesizes a local "Connected" signal mirroring the local "Disconnected" signal. This is called
+ * whenever we fully established a connection, i.e. after the authorization phase, and after receiving the
+ * Hello() reply. Or in other words, whenver we enter BUS_RUNNING state.
+ *
+ * This is useful so that clients can start doing stuff whenver the connection is fully established in a way
+ * that works independently from whether we connected to a full bus or just a direct connection. */
+
+ if (!bus->connected_signal)
+ return 0;
+
+ r = sd_bus_message_new_signal(
+ bus,
+ &m,
+ "/org/freedesktop/DBus/Local",
+ "org.freedesktop.DBus.Local",
+ "Connected");
+ if (r < 0)
+ return r;
+
+ bus_message_set_sender_local(bus, m);
+
+ r = bus_seal_synthetic_message(bus, m);
+ if (r < 0)
+ return r;
+
+ r = bus_rqueue_make_room(bus);
+ if (r < 0)
+ return r;
+
+ /* Insert at the very front */
+ memmove(bus->rqueue + 1, bus->rqueue, sizeof(sd_bus_message*) * bus->rqueue_size);
+ bus->rqueue[0] = m;
+ m = NULL;
+ bus->rqueue_size++;
+
+ return 0;
+}
+
+void bus_set_state(sd_bus *bus, enum bus_state state) {
+
+ static const char * const table[_BUS_STATE_MAX] = {
+ [BUS_UNSET] = "UNSET",
+ [BUS_WATCH_BIND] = "WATCH_BIND",
+ [BUS_OPENING] = "OPENING",
+ [BUS_AUTHENTICATING] = "AUTHENTICATING",
+ [BUS_HELLO] = "HELLO",
+ [BUS_RUNNING] = "RUNNING",
+ [BUS_CLOSING] = "CLOSING",
+ [BUS_CLOSED] = "CLOSED",
+ };
+
+ assert(bus);
+ assert(state < _BUS_STATE_MAX);
+
+ if (state == bus->state)
+ return;
+
+ log_debug("Bus %s: changing state %s → %s", strna(bus->description), table[bus->state], table[state]);
+ bus->state = state;
+}
+
static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
const char *s;
sd_bus *bus;
@@ -401,8 +579,13 @@ static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *e
if (!bus->unique_name)
return -ENOMEM;
- if (bus->state == BUS_HELLO)
- bus->state = BUS_RUNNING;
+ if (bus->state == BUS_HELLO) {
+ bus_set_state(bus, BUS_RUNNING);
+
+ r = synthesize_connected_signal(bus);
+ if (r < 0)
+ return r;
+ }
return 1;
}
@@ -430,14 +613,37 @@ static int bus_send_hello(sd_bus *bus) {
}
int bus_start_running(sd_bus *bus) {
+ struct reply_callback *c;
+ Iterator i;
+ usec_t n;
+ int r;
+
assert(bus);
+ assert(bus->state < BUS_HELLO);
+
+ /* We start all method call timeouts when we enter BUS_HELLO or BUS_RUNNING mode. At this point let's convert
+ * all relative to absolute timestamps. Note that we do not reshuffle the reply callback priority queue since
+ * adding a fixed value to all entries should not alter the internal order. */
+
+ n = now(CLOCK_MONOTONIC);
+ ORDERED_HASHMAP_FOREACH(c, bus->reply_callbacks, i) {
+ if (c->timeout_usec == 0)
+ continue;
+
+ c->timeout_usec = usec_add(n, c->timeout_usec);
+ }
if (bus->bus_client) {
- bus->state = BUS_HELLO;
+ bus_set_state(bus, BUS_HELLO);
return 1;
}
- bus->state = BUS_RUNNING;
+ bus_set_state(bus, BUS_RUNNING);
+
+ r = synthesize_connected_signal(bus);
+ if (r < 0)
+ return r;
+
return 1;
}
@@ -799,6 +1005,7 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid)
b->nspid = 0;
b->sockaddr.un.sun_family = AF_UNIX;
+ /* Note that we use the old /var/run prefix here, to increase compatibility with really old containers */
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);
b->is_local = false;
@@ -898,7 +1105,8 @@ static int bus_start_address(sd_bus *b) {
assert(b);
for (;;) {
- bus_close_fds(b);
+ bus_close_io_fds(b);
+ bus_close_inotify_fd(b);
/* If you provide multiple different bus-addresses, we
* try all of them in order and use the first one that
@@ -906,20 +1114,25 @@ static int bus_start_address(sd_bus *b) {
if (b->exec_path)
r = bus_socket_exec(b);
-
else if ((b->nspid > 0 || b->machine) && b->sockaddr.sa.sa_family != AF_UNSPEC)
r = bus_container_connect_socket(b);
-
else if (b->sockaddr.sa.sa_family != AF_UNSPEC)
r = bus_socket_connect(b);
-
else
goto next;
if (r >= 0) {
- r = attach_io_events(b);
- if (r >= 0)
- return r;
+ int q;
+
+ q = bus_attach_io_events(b);
+ if (q < 0)
+ return q;
+
+ q = bus_attach_inotify_event(b);
+ if (q < 0)
+ return q;
+
+ return r;
}
b->last_connect_error = -r;
@@ -976,10 +1189,11 @@ _public_ int sd_bus_start(sd_bus *bus) {
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
- bus->state = BUS_OPENING;
+ bus_set_state(bus, BUS_OPENING);
if (bus->is_server && bus->bus_client)
return -EINVAL;
@@ -1040,7 +1254,6 @@ _public_ int sd_bus_open(sd_bus **ret) {
* be safe, and authenticate everything */
b->trusted = false;
b->is_local = 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);
@@ -1086,7 +1299,6 @@ _public_ int sd_bus_open_system(sd_bus **ret) {
/* 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;
b->is_local = true;
@@ -1120,7 +1332,7 @@ int bus_set_address_user(sd_bus *b) {
if (!ee)
return -ENOMEM;
- if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, ee) < 0)
+ if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, ee) < 0)
return -ENOMEM;
b->address = s;
@@ -1294,7 +1506,7 @@ _public_ void sd_bus_close(sd_bus *bus) {
if (bus_pid_changed(bus))
return;
- bus->state = BUS_CLOSED;
+ bus_set_state(bus, BUS_CLOSED);
sd_bus_detach_event(bus);
@@ -1302,7 +1514,8 @@ _public_ void sd_bus_close(sd_bus *bus) {
* the bus object and the bus may be freed */
bus_reset_queues(bus);
- bus_close_fds(bus);
+ bus_close_io_fds(bus);
+ bus_close_inotify_fd(bus);
}
_public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) {
@@ -1316,13 +1529,13 @@ _public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) {
return sd_bus_unref(bus);
}
-static void bus_enter_closing(sd_bus *bus) {
+void bus_enter_closing(sd_bus *bus) {
assert(bus);
- if (!IN_SET(bus->state, BUS_OPENING, BUS_AUTHENTICATING, BUS_HELLO, BUS_RUNNING))
+ if (!IN_SET(bus->state, BUS_WATCH_BIND, BUS_OPENING, BUS_AUTHENTICATING, BUS_HELLO, BUS_RUNNING))
return;
- bus->state = BUS_CLOSING;
+ bus_set_state(bus, BUS_CLOSING);
}
_public_ sd_bus *sd_bus_ref(sd_bus *bus) {
@@ -1352,23 +1565,33 @@ _public_ sd_bus *sd_bus_unref(sd_bus *bus) {
_public_ int sd_bus_is_open(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return BUS_IS_OPEN(bus->state);
}
+_public_ int sd_bus_is_ready(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ return bus->state == BUS_RUNNING;
+}
+
_public_ int sd_bus_can_send(sd_bus *bus, char type) {
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state != BUS_UNSET, -ENOTCONN);
assert_return(!bus_pid_changed(bus), -ECHILD);
- if (bus->hello_flags & KDBUS_HELLO_MONITOR)
+ if (bus->is_monitor)
return 0;
if (type == SD_BUS_TYPE_UNIX_FD) {
- if (!(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD))
+ if (!bus->accept_fd)
return 0;
r = bus_ensure_running(bus);
@@ -1385,6 +1608,7 @@ _public_ int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id) {
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(id, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -1397,6 +1621,8 @@ _public_ int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id) {
}
static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) {
+ int r;
+
assert(b);
assert(m);
@@ -1411,6 +1637,12 @@ static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) {
if (timeout == 0)
timeout = BUS_DEFAULT_TIMEOUT;
+ if (!m->sender && b->patch_sender) {
+ r = sd_bus_message_set_sender(m, b->patch_sender);
+ if (r < 0)
+ return r;
+ }
+
return sd_bus_message_seal(m, ++b->cookie, timeout);
}
@@ -1436,7 +1668,7 @@ int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) {
/* Fake some timestamps, if they were requested, and not
* already initialized */
- if (b->attach_flags & KDBUS_ATTACH_TIMESTAMP) {
+ if (b->attach_timestamp) {
if (m->realtime <= 0)
m->realtime = now(CLOCK_REALTIME);
@@ -1454,7 +1686,7 @@ int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) {
return sd_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) {
+static int bus_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
int r;
assert(bus);
@@ -1465,7 +1697,7 @@ static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call
return r;
if (*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-name=%s error-message=%s",
+ log_debug("Sent message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s",
bus_message_type_to_string(m->header->type),
strna(sd_bus_message_get_sender(m)),
strna(sd_bus_message_get_destination(m)),
@@ -1474,6 +1706,7 @@ static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call
strna(sd_bus_message_get_member(m)),
BUS_MESSAGE_COOKIE(m),
m->reply_cookie,
+ strna(m->root_container.signature),
strna(m->error.name),
strna(m->error.message));
@@ -1488,7 +1721,7 @@ static int dispatch_wqueue(sd_bus *bus) {
while (bus->wqueue_size > 0) {
- r = bus_write_message(bus, bus->wqueue[0], false, &bus->windex);
+ r = bus_write_message(bus, bus->wqueue[0], &bus->windex);
if (r < 0)
return r;
else if (r == 0)
@@ -1567,7 +1800,7 @@ static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd
}
}
-static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, bool hint_sync_call) {
+_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m);
int r;
@@ -1612,7 +1845,7 @@ static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie,
if (IN_SET(bus->state, BUS_RUNNING, BUS_HELLO) && bus->wqueue_size <= 0) {
size_t idx = 0;
- r = bus_write_message(bus, m, hint_sync_call, &idx);
+ r = bus_write_message(bus, m, &idx);
if (r < 0) {
if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
bus_enter_closing(bus);
@@ -1652,10 +1885,6 @@ finish:
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;
@@ -1682,26 +1911,35 @@ _public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destinat
return sd_bus_send(bus, m, cookie);
}
-static usec_t calc_elapse(uint64_t usec) {
+static usec_t calc_elapse(sd_bus *bus, uint64_t usec) {
+ assert(bus);
+
if (usec == (uint64_t) -1)
return 0;
- return now(CLOCK_MONOTONIC) + usec;
+ /* We start all timeouts the instant we enter BUS_HELLO/BUS_RUNNING state, so that the don't run in parallel
+ * with any connection setup states. Hence, if a method callback is started earlier than that we just store the
+ * relative timestamp, and afterwards the absolute one. */
+
+ if (IN_SET(bus->state, BUS_WATCH_BIND, BUS_OPENING, BUS_AUTHENTICATING))
+ return usec;
+ else
+ 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)
+ if (x->timeout_usec != 0 && y->timeout_usec == 0)
return -1;
- if (x->timeout == 0 && y->timeout != 0)
+ if (x->timeout_usec == 0 && y->timeout_usec != 0)
return 1;
- if (x->timeout < y->timeout)
+ if (x->timeout_usec < y->timeout_usec)
return -1;
- if (x->timeout > y->timeout)
+ if (x->timeout_usec > y->timeout_usec)
return 1;
return 0;
@@ -1721,8 +1959,7 @@ _public_ int sd_bus_call_async(
assert_return(m, -EINVAL);
assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
- assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
- assert_return(callback, -EINVAL);
+ assert_return(!m->sealed || (!!callback == !(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)), -EINVAL);
if (!bus)
bus = m->bus;
@@ -1732,6 +1969,10 @@ _public_ int sd_bus_call_async(
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
+ /* If no callback is specified and there's no interest in a slot, then there's no reason to ask for a reply */
+ if (!callback && !slot && !m->sealed)
+ m->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
+
r = ordered_hashmap_ensure_allocated(&bus->reply_callbacks, &uint64_hash_ops);
if (r < 0)
return r;
@@ -1748,29 +1989,31 @@ _public_ int sd_bus_call_async(
if (r < 0)
return r;
- s = bus_slot_allocate(bus, !slot, BUS_REPLY_CALLBACK, sizeof(struct reply_callback), userdata);
- if (!s)
- return -ENOMEM;
+ if (slot || callback) {
+ 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.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);
+ 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.timeout = 0;
+ s->reply_callback.cookie = 0;
return r;
}
+
+ s->reply_callback.timeout_usec = calc_elapse(bus, m->timeout);
+ if (s->reply_callback.timeout_usec != 0) {
+ r = prioq_put(bus->reply_callbacks_prioq, &s->reply_callback, &s->reply_callback.prioq_idx);
+ if (r < 0) {
+ s->reply_callback.timeout_usec = 0;
+ return r;
+ }
+ }
}
- r = sd_bus_send(bus, m, &s->reply_callback.cookie);
+ r = sd_bus_send(bus, m, s ? &s->reply_callback.cookie : NULL);
if (r < 0)
return r;
@@ -1848,11 +2091,11 @@ _public_ int sd_bus_call(
if (r < 0)
goto fail;
- r = bus_send_internal(bus, m, &cookie, true);
+ r = sd_bus_send(bus, m, &cookie);
if (r < 0)
goto fail;
- timeout = calc_elapse(m->timeout);
+ timeout = calc_elapse(bus, m->timeout);
for (;;) {
usec_t left;
@@ -1871,7 +2114,7 @@ _public_ int sd_bus_call(
if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) {
- if (incoming->n_fds <= 0 || (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) {
+ if (incoming->n_fds <= 0 || bus->accept_fd) {
if (reply)
*reply = incoming;
else
@@ -1966,35 +2209,63 @@ fail:
_public_ int sd_bus_get_fd(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->input_fd == bus->output_fd, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
- return bus->input_fd;
+ if (bus->state == BUS_CLOSED)
+ return -ENOTCONN;
+
+ if (bus->inotify_fd >= 0)
+ return bus->inotify_fd;
+
+ if (bus->input_fd >= 0)
+ return bus->input_fd;
+
+ return -ENOTCONN;
}
_public_ int sd_bus_get_events(sd_bus *bus) {
int flags = 0;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
- if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING)
+ switch (bus->state) {
+
+ case BUS_UNSET:
+ case BUS_CLOSED:
return -ENOTCONN;
- if (bus->state == BUS_OPENING)
+ case BUS_WATCH_BIND:
+ flags |= POLLIN;
+ break;
+
+ case BUS_OPENING:
flags |= POLLOUT;
- else if (bus->state == BUS_AUTHENTICATING) {
+ break;
+ case BUS_AUTHENTICATING:
if (bus_socket_auth_needs_write(bus))
flags |= POLLOUT;
flags |= POLLIN;
+ break;
- } else if (IN_SET(bus->state, BUS_RUNNING, BUS_HELLO)) {
+ case BUS_RUNNING:
+ case BUS_HELLO:
if (bus->rqueue_size <= 0)
flags |= POLLIN;
if (bus->wqueue_size > 0)
flags |= POLLOUT;
+ break;
+
+ case BUS_CLOSING:
+ break;
+
+ default:
+ assert_not_reached("Unknown state");
}
return flags;
@@ -2004,6 +2275,7 @@ _public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
struct reply_callback *c;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(timeout_usec, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2015,39 +2287,45 @@ _public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
return 1;
}
- if (bus->state == BUS_CLOSING) {
- *timeout_usec = 0;
- return 1;
- }
+ switch (bus->state) {
- if (bus->state == BUS_AUTHENTICATING) {
+ case BUS_AUTHENTICATING:
*timeout_usec = bus->auth_timeout;
return 1;
- }
- if (!IN_SET(bus->state, BUS_RUNNING, BUS_HELLO)) {
- *timeout_usec = (uint64_t) -1;
- return 0;
- }
+ case BUS_RUNNING:
+ case BUS_HELLO:
+ 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_usec == 0) {
+ *timeout_usec = (uint64_t) -1;
+ return 0;
+ }
+
+ *timeout_usec = c->timeout_usec;
+ return 1;
- if (bus->rqueue_size > 0) {
+ case BUS_CLOSING:
*timeout_usec = 0;
return 1;
- }
- c = prioq_peek(bus->reply_callbacks_prioq);
- if (!c) {
+ case BUS_WATCH_BIND:
+ case BUS_OPENING:
*timeout_usec = (uint64_t) -1;
return 0;
- }
- if (c->timeout == 0) {
- *timeout_usec = (uint64_t) -1;
- return 0;
+ default:
+ assert_not_reached("Unknown or unexpected stat");
}
-
- *timeout_usec = c->timeout;
- return 1;
}
static int process_timeout(sd_bus *bus) {
@@ -2055,17 +2333,19 @@ static int process_timeout(sd_bus *bus) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message* m = NULL;
struct reply_callback *c;
sd_bus_slot *slot;
+ bool is_hello;
usec_t n;
int r;
assert(bus);
+ assert(IN_SET(bus->state, BUS_RUNNING, BUS_HELLO));
c = prioq_peek(bus->reply_callbacks_prioq);
if (!c)
return 0;
n = now(CLOCK_MONOTONIC);
- if (c->timeout > n)
+ if (c->timeout_usec > n)
return 0;
r = bus_message_new_synthetic_error(
@@ -2081,7 +2361,7 @@ static int process_timeout(sd_bus *bus) {
return r;
assert_se(prioq_pop(bus->reply_callbacks_prioq) == c);
- c->timeout = 0;
+ c->timeout_usec = 0;
ordered_hashmap_remove(bus->reply_callbacks, &c->cookie);
c->cookie = 0;
@@ -2090,6 +2370,8 @@ static int process_timeout(sd_bus *bus) {
bus->iteration_counter++;
+ is_hello = bus->state == BUS_HELLO && c->callback == hello_callback;
+
bus->current_message = m;
bus->current_slot = sd_bus_slot_ref(slot);
bus->current_handler = c->callback;
@@ -2107,6 +2389,11 @@ static int process_timeout(sd_bus *bus) {
sd_bus_slot_unref(slot);
+ /* When this is the hello message and it timed out, then make sure to propagate the error up, don't just log
+ * and ignore the callback handler's return value. */
+ if (is_hello)
+ return r;
+
return bus_maybe_reply_error(m, r, &error_buffer);
}
@@ -2136,6 +2423,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
_cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
struct reply_callback *c;
sd_bus_slot *slot;
+ bool is_hello;
int r;
assert(bus);
@@ -2155,7 +2443,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
slot = container_of(c, sd_bus_slot, reply_callback);
- if (m->n_fds > 0 && !(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) {
+ if (m->n_fds > 0 && !bus->accept_fd) {
/* If the reply contained a file descriptor which we
* didn't want we pass an error instead. */
@@ -2184,11 +2472,13 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
return r;
}
- if (c->timeout != 0) {
+ if (c->timeout_usec != 0) {
prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
- c->timeout = 0;
+ c->timeout_usec = 0;
}
+ is_hello = bus->state == BUS_HELLO && c->callback == hello_callback;
+
bus->current_slot = sd_bus_slot_ref(slot);
bus->current_handler = c->callback;
bus->current_userdata = slot->userdata;
@@ -2204,6 +2494,11 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
sd_bus_slot_unref(slot);
+ /* When this is the hello message and it failed, then make sure to propagate the error up, don't just log and
+ * ignore the callback handler's return value. */
+ if (is_hello)
+ return r;
+
return bus_maybe_reply_error(m, r, &error_buffer);
}
@@ -2280,7 +2575,7 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) {
assert(bus);
assert(m);
- if (bus->hello_flags & KDBUS_HELLO_MONITOR)
+ if (bus->is_monitor)
return 0;
if (bus->manual_peer_interface)
@@ -2338,13 +2633,13 @@ static int process_fd_check(sd_bus *bus, sd_bus_message *m) {
* delivered to us later even though we ourselves did not
* negotiate it. */
- if (bus->hello_flags & KDBUS_HELLO_MONITOR)
+ if (bus->is_monitor)
return 0;
if (m->n_fds <= 0)
return 0;
- if (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)
+ if (bus->accept_fd)
return 0;
if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
@@ -2515,9 +2810,9 @@ static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c)
if (r < 0)
return r;
- if (c->timeout != 0) {
+ if (c->timeout_usec != 0) {
prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
- c->timeout = 0;
+ c->timeout_usec = 0;
}
ordered_hashmap_remove(bus->reply_callbacks, &c->cookie);
@@ -2622,6 +2917,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit
* means *ret is filled in with an unprocessed message. */
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
/* We don't allow recursively invoking sd_bus_process(). */
@@ -2636,48 +2932,44 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit
case BUS_CLOSED:
return -ECONNRESET;
+ case BUS_WATCH_BIND:
+ r = bus_socket_process_watch_bind(bus);
+ break;
+
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;
+ break;
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;
+ break;
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;
- }
+ if (r >= 0)
+ return r;
- return r;
+ /* This branch initializes *ret, hence we don't use the generic error checking below */
+ break;
case BUS_CLOSING:
return process_closing(bus, ret);
+
+ default:
+ assert_not_reached("Unknown state");
}
- assert_not_reached("Unknown state");
+ 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;
}
_public_ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
@@ -2690,7 +2982,7 @@ _public_ int sd_bus_process_priority(sd_bus *bus, int64_t priority, sd_bus_messa
static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
struct pollfd p[2] = {};
- int r, e, n;
+ int r, n;
struct timespec ts;
usec_t m = USEC_INFINITY;
@@ -2702,45 +2994,52 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
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 (bus->state == BUS_WATCH_BIND) {
+ assert(bus->inotify_fd >= 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;
+ p[0].events = POLLIN;
+ p[0].fd = bus->inotify_fd;
n = 1;
} else {
- p[0].events = e & POLLIN;
- p[1].fd = bus->output_fd;
- p[1].events = e & POLLOUT;
- n = 2;
+ int e;
+
+ 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)
+ m = usec_sub_unsigned(until, now(CLOCK_MONOTONIC));
+ }
+
+ 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 (timeout_usec != (uint64_t) -1 && (m == USEC_INFINITY || timeout_usec < m))
+ m = timeout_usec;
+
+ r = ppoll(p, n, m == USEC_INFINITY ? NULL : timespec_store(&ts, m), NULL);
if (r < 0)
return -errno;
@@ -2750,6 +3049,7 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
_public_ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
if (bus->state == BUS_CLOSING)
@@ -2768,6 +3068,7 @@ _public_ int sd_bus_flush(sd_bus *bus) {
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
if (bus->state == BUS_CLOSING)
@@ -2776,6 +3077,10 @@ _public_ int sd_bus_flush(sd_bus *bus) {
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
+ /* We never were connected? Don't hang in inotify for good, as there's no timeout set for it */
+ if (bus->state == BUS_WATCH_BIND)
+ return -EUNATCH;
+
r = bus_ensure_running(bus);
if (r < 0)
return r;
@@ -2812,6 +3117,7 @@ _public_ int sd_bus_add_filter(
sd_bus_slot *s;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(callback, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2830,11 +3136,78 @@ _public_ int sd_bus_add_filter(
return 0;
}
-_public_ int sd_bus_add_match(
+static int add_match_callback(
+ sd_bus_message *m,
+ void *userdata,
+ sd_bus_error *ret_error) {
+
+ sd_bus_slot *match_slot = userdata;
+ bool failed = false;
+ int r;
+
+ assert(m);
+ assert(match_slot);
+
+ sd_bus_slot_ref(match_slot);
+
+ if (sd_bus_message_is_method_error(m, NULL)) {
+ log_debug_errno(sd_bus_message_get_errno(m),
+ "Unable to add match %s, failing connection: %s",
+ match_slot->match_callback.match_string,
+ sd_bus_message_get_error(m)->message);
+
+ failed = true;
+ } else
+ log_debug("Match %s successfully installed.", match_slot->match_callback.match_string);
+
+ if (match_slot->match_callback.install_callback) {
+ sd_bus *bus;
+
+ bus = sd_bus_message_get_bus(m);
+
+ /* This function has been called as slot handler, and we want to call another slot handler. Let's
+ * update the slot callback metadata temporarily with our own data, and then revert back to the old
+ * values. */
+
+ assert(bus->current_slot == match_slot->match_callback.install_slot);
+ assert(bus->current_handler == add_match_callback);
+ assert(bus->current_userdata == userdata);
+
+ bus->current_slot = match_slot;
+ bus->current_handler = match_slot->match_callback.install_callback;
+ bus->current_userdata = match_slot->userdata;
+
+ r = match_slot->match_callback.install_callback(m, match_slot->userdata, ret_error);
+
+ bus->current_slot = match_slot->match_callback.install_slot;
+ bus->current_handler = add_match_callback;
+ bus->current_userdata = userdata;
+
+ match_slot->match_callback.install_slot = sd_bus_slot_unref(match_slot->match_callback.install_slot);
+ } else {
+ if (failed) /* Generic failure handling: destroy the connection */
+ bus_enter_closing(sd_bus_message_get_bus(m));
+
+ r = 1;
+ }
+
+ if (failed && match_slot->floating) {
+ bus_slot_disconnect(match_slot);
+ sd_bus_slot_unref(match_slot);
+ }
+
+ sd_bus_slot_unref(match_slot);
+
+ return r;
+}
+
+static int bus_add_match_full(
sd_bus *bus,
sd_bus_slot **slot,
+ bool asynchronous,
const char *match,
sd_bus_message_handler_t callback,
+ sd_bus_message_handler_t install_callback,
void *userdata) {
struct bus_match_component *components = NULL;
@@ -2843,6 +3216,7 @@ _public_ int sd_bus_add_match(
int r = 0;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(match, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2857,18 +3231,17 @@ _public_ int sd_bus_add_match(
}
s->match_callback.callback = callback;
+ s->match_callback.install_callback = install_callback;
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. */
+ /* Do not install server-side matches for matches against the local service, interface or bus path. */
if (scope != BUS_MATCH_LOCAL) {
- /* We store the original match string, so that
- * we can use it to remove the match again. */
+ /* 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) {
@@ -2876,7 +3249,14 @@ _public_ int sd_bus_add_match(
goto finish;
}
- r = bus_add_match_internal(bus, s->match_callback.match_string, components, n_components);
+ if (asynchronous)
+ r = bus_add_match_internal_async(bus,
+ &s->match_callback.install_slot,
+ s->match_callback.match_string,
+ add_match_callback,
+ s);
+ else
+ r = bus_add_match_internal(bus, s->match_callback.match_string);
if (r < 0)
goto finish;
@@ -2900,35 +3280,25 @@ finish:
return r;
}
-int bus_remove_match_by_string(
+_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;
- 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));
+ return bus_add_match_full(bus, slot, false, match, callback, NULL, userdata);
+}
-finish:
- bus_match_parse_free(components, n_components);
+_public_ int sd_bus_add_match_async(
+ sd_bus *bus,
+ sd_bus_slot **slot,
+ const char *match,
+ sd_bus_message_handler_t callback,
+ sd_bus_message_handler_t install_callback,
+ void *userdata) {
- return r;
+ return bus_add_match_full(bus, slot, true, match, callback, install_callback, userdata);
}
bool bus_pid_changed(sd_bus *bus) {
@@ -2946,9 +3316,13 @@ static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userd
assert(bus);
+ /* Note that this is called both on input_fd, output_fd as well as inotify_fd events */
+
r = sd_bus_process(bus, NULL);
- if (r < 0)
- return r;
+ if (r < 0) {
+ log_debug_errno(r, "Processing of bus failed, closing down: %m");
+ bus_enter_closing(bus);
+ }
return 1;
}
@@ -2960,8 +3334,10 @@ static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
assert(bus);
r = sd_bus_process(bus, NULL);
- if (r < 0)
- return r;
+ if (r < 0) {
+ log_debug_errno(r, "Processing of bus failed, closing down: %m");
+ bus_enter_closing(bus);
+ }
return 1;
}
@@ -2975,38 +3351,45 @@ static int prepare_callback(sd_event_source *s, void *userdata) {
assert(bus);
e = sd_bus_get_events(bus);
- if (e < 0)
- return e;
+ if (e < 0) {
+ r = e;
+ goto fail;
+ }
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;
+ goto fail;
r = sd_event_source_set_io_events(bus->output_io_event_source, e & POLLOUT);
- if (r < 0)
- return r;
- } else {
+ } else
r = sd_event_source_set_io_events(bus->input_io_event_source, e);
- if (r < 0)
- return r;
- }
+ if (r < 0)
+ goto fail;
r = sd_bus_get_timeout(bus, &until);
if (r < 0)
- return r;
+ goto fail;
if (r > 0) {
int j;
j = sd_event_source_set_time(bus->time_event_source, until);
- if (j < 0)
- return j;
+ if (j < 0) {
+ r = j;
+ goto fail;
+ }
}
r = sd_event_source_set_enabled(bus->time_event_source, r > 0);
if (r < 0)
- return r;
+ goto fail;
+
+ return 1;
+
+fail:
+ log_debug_errno(r, "Preparing of bus events failed, closing down: %m");
+ bus_enter_closing(bus);
return 1;
}
@@ -3022,7 +3405,7 @@ static int quit_callback(sd_event_source *event, void *userdata) {
return 1;
}
-static int attach_io_events(sd_bus *bus) {
+int bus_attach_io_events(sd_bus *bus) {
int r;
assert(bus);
@@ -3076,7 +3459,7 @@ static int attach_io_events(sd_bus *bus) {
return 0;
}
-static void detach_io_events(sd_bus *bus) {
+static void bus_detach_io_events(sd_bus *bus) {
assert(bus);
if (bus->input_io_event_source) {
@@ -3090,10 +3473,49 @@ static void detach_io_events(sd_bus *bus) {
}
}
+int bus_attach_inotify_event(sd_bus *bus) {
+ int r;
+
+ assert(bus);
+
+ if (bus->inotify_fd < 0)
+ return 0;
+
+ if (!bus->event)
+ return 0;
+
+ if (!bus->inotify_event_source) {
+ r = sd_event_add_io(bus->event, &bus->inotify_event_source, bus->inotify_fd, EPOLLIN, io_callback, bus);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(bus->inotify_event_source, bus->event_priority);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_description(bus->inotify_event_source, "bus-inotify");
+ } else
+ r = sd_event_source_set_io_fd(bus->inotify_event_source, bus->inotify_fd);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static void bus_detach_inotify_event(sd_bus *bus) {
+ assert(bus);
+
+ if (bus->inotify_event_source) {
+ sd_event_source_set_enabled(bus->inotify_event_source, SD_EVENT_OFF);
+ bus->inotify_event_source = sd_event_source_unref(bus->inotify_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 = bus_resolve(bus), -ENOPKG);
assert_return(!bus->event, -EBUSY);
assert(!bus->input_io_event_source);
@@ -3130,7 +3552,11 @@ _public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) {
if (r < 0)
goto fail;
- r = attach_io_events(bus);
+ r = bus_attach_io_events(bus);
+ if (r < 0)
+ goto fail;
+
+ r = bus_attach_inotify_event(bus);
if (r < 0)
goto fail;
@@ -3143,11 +3569,13 @@ fail:
_public_ int sd_bus_detach_event(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
if (!bus->event)
return 0;
- detach_io_events(bus);
+ bus_detach_io_events(bus);
+ bus_detach_inotify_event(bus);
if (bus->time_event_source) {
sd_event_source_set_enabled(bus->time_event_source, SD_EVENT_OFF);
@@ -3230,39 +3658,11 @@ _public_ int sd_bus_default_user(sd_bus **ret) {
}
_public_ int sd_bus_default(sd_bus **ret) {
+ int (*bus_open)(sd_bus **) = NULL;
+ sd_bus **busp;
- 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);
+ busp = bus_choose_default(&bus_open);
+ return bus_default(bus_open, busp, ret);
}
_public_ int sd_bus_get_tid(sd_bus *b, pid_t *tid) {
@@ -3483,13 +3883,13 @@ _public_ int sd_bus_path_decode_many(const char *path, const char *path_template
}
va_end(list);
- free(labels);
- labels = NULL;
+ labels = mfree(labels);
return 1;
}
_public_ int sd_bus_try_close(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return -EOPNOTSUPP;
@@ -3497,6 +3897,7 @@ _public_ int sd_bus_try_close(sd_bus *bus) {
_public_ int sd_bus_get_description(sd_bus *bus, const char **description) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(description, -EINVAL);
assert_return(bus->description, -ENXIO);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -3525,6 +3926,7 @@ int bus_get_root_path(sd_bus *bus) {
_public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(scope, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -3544,6 +3946,7 @@ _public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) {
_public_ int sd_bus_get_address(sd_bus *bus, const char **address) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(address, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -3557,6 +3960,7 @@ _public_ int sd_bus_get_address(sd_bus *bus, const char **address) {
_public_ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(mask, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -3566,6 +3970,7 @@ _public_ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) {
_public_ int sd_bus_is_bus_client(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->bus_client;
@@ -3573,6 +3978,7 @@ _public_ int sd_bus_is_bus_client(sd_bus *bus) {
_public_ int sd_bus_is_server(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->is_server;
@@ -3580,6 +3986,7 @@ _public_ int sd_bus_is_server(sd_bus *bus) {
_public_ int sd_bus_is_anonymous(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->anonymous_auth;
@@ -3587,6 +3994,7 @@ _public_ int sd_bus_is_anonymous(sd_bus *bus) {
_public_ int sd_bus_is_trusted(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->trusted;
@@ -3594,9 +4002,10 @@ _public_ int sd_bus_is_trusted(sd_bus *bus) {
_public_ int sd_bus_is_monitor(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
- return !!(bus->hello_flags & KDBUS_HELLO_MONITOR);
+ return bus->is_monitor;
}
static void flush_close(sd_bus *bus) {
@@ -3618,6 +4027,7 @@ _public_ void sd_bus_default_flush_close(void) {
_public_ int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
/* Turns on exit-on-disconnect, and triggers it immediately if the bus connection was already
* disconnected. Note that this is triggered exclusively on disconnections triggered by the server side, never
@@ -3630,6 +4040,28 @@ _public_ int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b) {
_public_ int sd_bus_get_exit_on_disconnect(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
return bus->exit_on_disconnect;
}
+
+_public_ int sd_bus_set_sender(sd_bus *bus, const char *sender) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(!bus->bus_client, -EPERM);
+ assert_return(!sender || service_name_is_valid(sender), -EINVAL);
+
+ return free_and_strdup(&bus->patch_sender, sender);
+}
+
+_public_ int sd_bus_get_sender(sd_bus *bus, const char **ret) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(ret, -EINVAL);
+
+ if (!bus->patch_sender)
+ return -ENODATA;
+
+ *ret = bus->patch_sender;
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-benchmark.c b/src/libsystemd/sd-bus/test-bus-benchmark.c
index a466cddd75..bfd0f39372 100644
--- a/src/libsystemd/sd-bus/test-bus-benchmark.c
+++ b/src/libsystemd/sd-bus/test-bus-benchmark.c
@@ -319,7 +319,7 @@ int main(int argc, char *argv[]) {
break;
}
- _exit(0);
+ _exit(EXIT_SUCCESS);
}
CPU_ZERO(&cpuset);
diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c
index 1b2efb9bb4..bd6721946a 100644
--- a/src/libsystemd/sd-bus/test-bus-chat.c
+++ b/src/libsystemd/sd-bus/test-bus-chat.c
@@ -102,9 +102,9 @@ static int server_init(sd_bus **_bus) {
goto fail;
}
- r = sd_bus_add_match(bus, NULL, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL);
+ r = sd_bus_match_signal(bus, NULL, NULL, NULL, "foo.bar", "Notify", match_callback, NULL);
if (r < 0) {
- log_error_errno(r, "Failed to add match: %m");
+ log_error_errno(r, "Failed to request match: %m");
goto fail;
}
diff --git a/src/libsystemd/sd-bus/test-bus-track.c b/src/libsystemd/sd-bus/test-bus-track.c
index 320e8347f4..94c9d09de3 100644
--- a/src/libsystemd/sd-bus/test-bus-track.c
+++ b/src/libsystemd/sd-bus/test-bus-track.c
@@ -18,6 +18,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <sys/socket.h>
+
#include "sd-bus.h"
#include "macro.h"
diff --git a/src/libsystemd/sd-bus/test-bus-watch-bind.c b/src/libsystemd/sd-bus/test-bus-watch-bind.c
new file mode 100644
index 0000000000..aef5ba9486
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-watch-bind.c
@@ -0,0 +1,239 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/***
+ This file is part of systemd.
+
+ Copyright 2017 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pthread.h>
+
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "random-util.h"
+#include "rm-rf.h"
+#include "socket-util.h"
+#include "string-util.h"
+
+static int method_foobar(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ log_info("Got Foobar() call.");
+
+ assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0);
+ return sd_bus_reply_method_return(m, NULL);
+}
+
+static int method_exit(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ log_info("Got Exit() call");
+ assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 1) >= 0);
+ return sd_bus_reply_method_return(m, NULL);
+}
+
+static const sd_bus_vtable vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ SD_BUS_METHOD("Foobar", NULL, NULL, method_foobar, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Exit", NULL, NULL, method_exit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_VTABLE_END,
+};
+
+static void* thread_server(void *p) {
+ _cleanup_free_ char *suffixed = NULL, *suffixed2 = NULL, *d = NULL;
+ _cleanup_close_ int fd = -1;
+ union sockaddr_union u = {
+ .un.sun_family = AF_UNIX,
+ };
+ const char *path = p;
+
+ log_debug("Initializing server");
+
+ /* Let's play some games, by slowly creating the socket directory, and renaming it in the middle */
+ (void) usleep(100 * USEC_PER_MSEC);
+
+ assert_se(mkdir_parents(path, 0755) >= 0);
+ (void) usleep(100 * USEC_PER_MSEC);
+
+ d = dirname_malloc(path);
+ assert_se(d);
+ assert_se(asprintf(&suffixed, "%s.%" PRIx64, d, random_u64()) >= 0);
+ assert_se(rename(d, suffixed) >= 0);
+ (void) usleep(100 * USEC_PER_MSEC);
+
+ assert_se(asprintf(&suffixed2, "%s.%" PRIx64, d, random_u64()) >= 0);
+ assert_se(symlink(suffixed2, d) >= 0);
+ (void) usleep(100 * USEC_PER_MSEC);
+
+ assert_se(symlink(basename(suffixed), suffixed2) >= 0);
+ (void) usleep(100 * USEC_PER_MSEC);
+
+ strncpy(u.un.sun_path, path, sizeof(u.un.sun_path));
+
+ fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+ assert_se(fd >= 0);
+
+ assert_se(bind(fd, &u.sa, SOCKADDR_UN_LEN(u.un)) >= 0);
+ usleep(100 * USEC_PER_MSEC);
+
+ assert_se(listen(fd, SOMAXCONN) >= 0);
+ usleep(100 * USEC_PER_MSEC);
+
+ assert_se(touch(path) >= 0);
+ usleep(100 * USEC_PER_MSEC);
+
+ log_debug("Initialized server");
+
+ for (;;) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ sd_id128_t id;
+ int bus_fd, code;
+
+ assert_se(sd_id128_randomize(&id) >= 0);
+
+ assert_se(sd_event_new(&event) >= 0);
+
+ bus_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+ assert_se(bus_fd >= 0);
+
+ log_debug("Accepted server connection");
+
+ assert_se(sd_bus_new(&bus) >= 0);
+ assert_se(sd_bus_set_description(bus, "server") >= 0);
+ assert_se(sd_bus_set_fd(bus, bus_fd, bus_fd) >= 0);
+ assert_se(sd_bus_set_server(bus, true, id) >= 0);
+ /* assert_se(sd_bus_set_anonymous(bus, true) >= 0); */
+
+ assert_se(sd_bus_attach_event(bus, event, 0) >= 0);
+
+ assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "foo.TestInterface", vtable, NULL) >= 0);
+
+ assert_se(sd_bus_start(bus) >= 0);
+
+ assert_se(sd_event_loop(event) >= 0);
+
+ assert_se(sd_event_get_exit_code(event, &code) >= 0);
+
+ if (code > 0)
+ break;
+ }
+
+ log_debug("Server done");
+
+ return NULL;
+}
+
+static void* thread_client1(void *p) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ const char *path = p, *t;
+ int r;
+
+ log_debug("Initializing client1");
+
+ assert_se(sd_bus_new(&bus) >= 0);
+ assert_se(sd_bus_set_description(bus, "client1") >= 0);
+
+ t = strjoina("unix:path=", path);
+ assert_se(sd_bus_set_address(bus, t) >= 0);
+ assert_se(sd_bus_set_watch_bind(bus, true) >= 0);
+ assert_se(sd_bus_start(bus) >= 0);
+
+ r = sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Foobar", &error, NULL, NULL);
+ assert_se(r >= 0);
+
+ log_debug("Client1 done");
+
+ return NULL;
+}
+
+static int client2_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ assert_se(sd_bus_message_is_method_error(m, NULL) == 0);
+ assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0);
+ return 0;
+}
+
+static void* thread_client2(void *p) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ const char *path = p, *t;
+
+ log_debug("Initializing client2");
+
+ assert_se(sd_event_new(&event) >= 0);
+ assert_se(sd_bus_new(&bus) >= 0);
+ assert_se(sd_bus_set_description(bus, "client2") >= 0);
+
+ t = strjoina("unix:path=", path);
+ assert_se(sd_bus_set_address(bus, t) >= 0);
+ assert_se(sd_bus_set_watch_bind(bus, true) >= 0);
+ assert_se(sd_bus_attach_event(bus, event, 0) >= 0);
+ assert_se(sd_bus_start(bus) >= 0);
+
+ assert_se(sd_bus_call_method_async(bus, NULL, "foo.bar", "/foo", "foo.TestInterface", "Foobar", client2_callback, NULL, NULL) >= 0);
+
+ assert_se(sd_event_loop(event) >= 0);
+
+ log_debug("Client2 done");
+
+ return NULL;
+}
+
+static void request_exit(const char *path) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ const char *t;
+
+ assert_se(sd_bus_new(&bus) >= 0);
+
+ t = strjoina("unix:path=", path);
+ assert_se(sd_bus_set_address(bus, t) >= 0);
+ assert_se(sd_bus_set_watch_bind(bus, true) >= 0);
+ assert_se(sd_bus_set_description(bus, "request-exit") >= 0);
+ assert_se(sd_bus_start(bus) >= 0);
+
+ assert_se(sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Exit", NULL, NULL, NULL) >= 0);
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
+ pthread_t server, client1, client2;
+ char *path;
+
+ log_set_max_level(LOG_DEBUG);
+
+ /* We use /dev/shm here rather than /tmp, since some weird distros might set up /tmp as some weird fs that
+ * doesn't support inotify properly. */
+ assert_se(mkdtemp_malloc("/dev/shm/systemd-watch-bind-XXXXXX", &d) >= 0);
+
+ path = strjoina(d, "/this/is/a/socket");
+
+ assert_se(pthread_create(&server, NULL, thread_server, path) == 0);
+ assert_se(pthread_create(&client1, NULL, thread_client1, path) == 0);
+ assert_se(pthread_create(&client2, NULL, thread_client2, path) == 0);
+
+ assert_se(pthread_join(client1, NULL) == 0);
+ assert_se(pthread_join(client2, NULL) == 0);
+
+ request_exit(path);
+
+ assert_se(pthread_join(server, NULL) == 0);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c
index 64a74929bf..1334498ca4 100644
--- a/src/libsystemd/sd-daemon/sd-daemon.c
+++ b/src/libsystemd/sd-daemon/sd-daemon.c
@@ -39,6 +39,7 @@
#include "fs-util.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "socket-util.h"
#include "strv.h"
#include "util.h"
@@ -306,17 +307,13 @@ _public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint
return 0;
if (port > 0) {
- if (sockaddr.sa.sa_family == AF_INET) {
- if (l < sizeof(struct sockaddr_in))
- return -EINVAL;
+ unsigned sa_port;
- return htobe16(port) == sockaddr.in.sin_port;
- } else {
- if (l < sizeof(struct sockaddr_in6))
- return -EINVAL;
+ r = sockaddr_port(&sockaddr.sa, &sa_port);
+ if (r < 0)
+ return r;
- return htobe16(port) == sockaddr.in6.sin6_port;
- }
+ return port == sa_port;
}
return 1;
@@ -459,7 +456,13 @@ _public_ int sd_is_mq(int fd, const char *path) {
return 1;
}
-_public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) {
+_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,
};
@@ -474,7 +477,7 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char
_cleanup_close_ int fd = -1;
struct cmsghdr *cmsg = NULL;
const char *e;
- bool have_pid;
+ bool send_ucred;
int r;
if (!state) {
@@ -508,7 +511,7 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char
goto finish;
}
- fd_inc_sndbuf(fd, SNDBUF_SIZE);
+ (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
iovec.iov_len = strlen(state);
@@ -518,13 +521,16 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char
msghdr.msg_namelen = SOCKADDR_UN_LEN(sockaddr.un);
- have_pid = pid != 0 && pid != getpid_cached();
+ send_ucred =
+ (pid != 0 && pid != getpid_cached()) ||
+ getuid() != geteuid() ||
+ getgid() != getegid();
- if (n_fds > 0 || have_pid) {
+ if (n_fds > 0 || send_ucred) {
/* 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);
+ (send_ucred ? CMSG_SPACE(sizeof(struct ucred)) : 0);
msghdr.msg_control = alloca0(msghdr.msg_controllen);
@@ -536,11 +542,11 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char
memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
- if (have_pid)
+ if (send_ucred)
assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
}
- if (have_pid) {
+ if (send_ucred) {
struct ucred *ucred;
cmsg->cmsg_level = SOL_SOCKET;
@@ -548,7 +554,7 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
ucred = (struct ucred*) CMSG_DATA(cmsg);
- ucred->pid = pid;
+ ucred->pid = pid != 0 ? pid : getpid_cached();
ucred->uid = getuid();
ucred->gid = getgid();
}
@@ -561,7 +567,7 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char
}
/* If that failed, try with our own ucred instead */
- if (have_pid) {
+ if (send_ucred) {
msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
if (msghdr.msg_controllen == 0)
msghdr.msg_control = NULL;
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index a96d7445ce..1297dfa911 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -166,25 +166,38 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
}
if (verify) {
- r = readlink_and_canonicalize(_syspath, NULL, &syspath);
+ r = chase_symlinks(_syspath, NULL, 0, &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) {
+ 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")) {
+ _cleanup_free_ char *real_sys = NULL, *new_syspath = NULL;
+ char *p;
+
+ /* /sys is a symlink to somewhere sysfs is mounted on? In that case, we convert the path to real sysfs to "/sys". */
+ r = chase_symlinks("/sys", NULL, 0, &real_sys);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: could not chase symlink /sys: %m");
+
+ p = path_startswith(syspath, real_sys);
+ if (!p) {
+ log_debug("sd-device: canonicalized path '%s' does not starts with sysfs mount point '%s'", syspath, real_sys);
+ return -ENODEV;
+ }
+
+ new_syspath = strjoin("/sys/", p);
+ if (!new_syspath)
+ return log_oom();
+
+ free_and_replace(syspath, new_syspath);
+ path_kill_slashes(syspath);
+ }
+
if (path_startswith(syspath, "/sys/devices/")) {
char *path;
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index a5f3e854b1..cb9b3a4545 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -122,6 +122,7 @@ struct sd_event_source {
uint32_t events;
uint32_t revents;
bool registered:1;
+ bool owned:1;
} io;
struct {
sd_event_time_handler_t callback;
@@ -240,8 +241,14 @@ struct sd_event {
unsigned delays[sizeof(usec_t) * 8];
};
+static thread_local sd_event *default_event = NULL;
+
static void source_disconnect(sd_event_source *s);
+static sd_event *event_resolve(sd_event *e) {
+ return e == SD_EVENT_DEFAULT ? default_event : e;
+}
+
static int pending_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b;
@@ -883,6 +890,10 @@ static void source_free(sd_event_source *s) {
assert(s);
source_disconnect(s);
+
+ if (s->type == SOURCE_IO && s->io.owned)
+ safe_close(s->io.fd);
+
free(s->description);
free(s);
}
@@ -967,6 +978,7 @@ _public_ int sd_event_add_io(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(fd >= 0, -EBADF);
assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
assert_return(callback, -EINVAL);
@@ -1067,6 +1079,7 @@ _public_ int sd_event_add_time(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(accuracy != (uint64_t) -1, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -1148,6 +1161,7 @@ _public_ int sd_event_add_signal(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(SIGNAL_VALID(sig), -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -1207,6 +1221,7 @@ _public_ int sd_event_add_child(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(pid > 1, -EINVAL);
assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL);
assert_return(options != 0, -EINVAL);
@@ -1264,6 +1279,7 @@ _public_ int sd_event_add_defer(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(callback, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -1298,6 +1314,7 @@ _public_ int sd_event_add_post(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(callback, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -1336,6 +1353,7 @@ _public_ int sd_event_add_exit(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(callback, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -1481,6 +1499,21 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
return 0;
}
+_public_ int sd_event_source_get_io_fd_own(sd_event_source *s) {
+ assert_return(s, -EINVAL);
+ assert_return(s->type == SOURCE_IO, -EDOM);
+
+ return s->io.owned;
+}
+
+_public_ int sd_event_source_set_io_fd_own(sd_event_source *s, int own) {
+ assert_return(s, -EINVAL);
+ assert_return(s->type == SOURCE_IO, -EDOM);
+
+ s->io.owned = own;
+ 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);
@@ -2449,6 +2482,7 @@ _public_ int sd_event_prepare(sd_event *e) {
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
@@ -2506,6 +2540,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
int r, m, i;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_ARMED, -EBUSY);
@@ -2612,6 +2647,7 @@ _public_ int sd_event_dispatch(sd_event *e) {
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_PENDING, -EBUSY);
@@ -2653,6 +2689,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
@@ -2697,6 +2734,7 @@ _public_ int sd_event_loop(sd_event *e) {
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
@@ -2718,6 +2756,7 @@ finish:
_public_ int sd_event_get_fd(sd_event *e) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
return e->epoll_fd;
@@ -2725,6 +2764,7 @@ _public_ int sd_event_get_fd(sd_event *e) {
_public_ int sd_event_get_state(sd_event *e) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
return e->state;
@@ -2732,6 +2772,7 @@ _public_ int sd_event_get_state(sd_event *e) {
_public_ int sd_event_get_exit_code(sd_event *e, int *code) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(code, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -2744,6 +2785,7 @@ _public_ int sd_event_get_exit_code(sd_event *e, int *code) {
_public_ int sd_event_exit(sd_event *e, int code) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -2755,6 +2797,7 @@ _public_ int sd_event_exit(sd_event *e, int code) {
_public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(usec, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -2779,8 +2822,6 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
}
_public_ int sd_event_default(sd_event **ret) {
-
- static thread_local sd_event *default_event = NULL;
sd_event *e = NULL;
int r;
@@ -2806,6 +2847,7 @@ _public_ int sd_event_default(sd_event **ret) {
_public_ int sd_event_get_tid(sd_event *e, pid_t *tid) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(tid, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -2821,6 +2863,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
if (e->watchdog == !!b)
@@ -2871,6 +2914,7 @@ fail:
_public_ int sd_event_get_watchdog(sd_event *e) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
return e->watchdog;
@@ -2878,6 +2922,7 @@ _public_ int sd_event_get_watchdog(sd_event *e) {
_public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
*ret = e->iteration;
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index 7f32838c6f..9873ae4a58 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -27,6 +27,7 @@
#include "macro.h"
#include "signal-util.h"
#include "util.h"
+#include "process-util.h"
static int prepare_handler(sd_event_source *s, void *userdata) {
log_info("preparing %c", PTR_TO_INT(userdata));
@@ -97,7 +98,7 @@ static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si,
assert_se(pid >= 0);
if (pid == 0)
- _exit(0);
+ _exit(EXIT_SUCCESS);
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);
diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c
index 5541e8d47e..a6e38578b1 100644
--- a/src/libsystemd/sd-id128/id128-util.c
+++ b/src/libsystemd/sd-id128/id128-util.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index e8adaa6823..69572d1a51 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -1061,10 +1061,15 @@ _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
}
_public_ int sd_login_monitor_flush(sd_login_monitor *m) {
+ int r;
assert_return(m, -EINVAL);
- return flush_fd(MONITOR_TO_FD(m));
+ r = flush_fd(MONITOR_TO_FD(m));
+ if (r < 0)
+ return r;
+
+ return 0;
}
_public_ int sd_login_monitor_get_fd(sd_login_monitor *m) {
diff --git a/src/libsystemd/sd-netlink/generic-netlink.c b/src/libsystemd/sd-netlink/generic-netlink.c
new file mode 100644
index 0000000000..771658d9ae
--- /dev/null
+++ b/src/libsystemd/sd-netlink/generic-netlink.c
@@ -0,0 +1,97 @@
+#include <linux/genetlink.h>
+
+#include "sd-netlink.h"
+#include "netlink-internal.h"
+#include "alloc-util.h"
+
+typedef struct {
+ const char* name;
+ uint8_t version;
+} genl_family;
+
+static const genl_family genl_families[] = {
+ [SD_GENL_ID_CTRL] = { .name = "", .version = 1 },
+ [SD_GENL_WIREGUARD] = { .name = "wireguard", .version = 1 },
+};
+
+int sd_genl_socket_open(sd_netlink **ret) {
+ return netlink_open_family(ret, NETLINK_GENERIC);
+}
+static int lookup_id(sd_netlink *nl, sd_genl_family family, uint16_t *id);
+
+static int genl_message_new(sd_netlink *nl, sd_genl_family family, uint16_t nlmsg_type, uint8_t cmd, sd_netlink_message **ret) {
+ int r;
+ struct genlmsghdr *genl;
+ const NLType *genl_cmd_type, *nl_type;
+ const NLTypeSystem *type_system;
+ size_t size;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+
+ assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
+
+ r = type_system_get_type(&genl_family_type_system_root, &genl_cmd_type, family);
+ if (r < 0)
+ return r;
+
+ r = message_new_empty(nl, &m);
+ if (r < 0)
+ return r;
+
+ size = NLMSG_SPACE(sizeof(struct genlmsghdr));
+ m->hdr = malloc0(size);
+ if (!m->hdr)
+ return -ENOMEM;
+
+ m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+
+ type_get_type_system(genl_cmd_type, &type_system);
+
+ r = type_system_get_type(type_system, &nl_type, cmd);
+ if (r < 0)
+ return r;
+
+ m->hdr->nlmsg_len = size;
+ m->hdr->nlmsg_type = nlmsg_type;
+
+ type_get_type_system(nl_type, &m->containers[0].type_system);
+ genl = NLMSG_DATA(m->hdr);
+ genl->cmd = cmd;
+ genl->version = genl_families[family].version;
+
+ *ret = m;
+ m = NULL;
+
+ return 0;
+}
+
+int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **ret) {
+ int r;
+ uint16_t id = GENL_ID_CTRL;
+
+ if (family != SD_GENL_ID_CTRL) {
+ r = lookup_id(nl, family, &id);
+ if (r < 0)
+ return r;
+ }
+
+ return genl_message_new(nl, family, id, cmd, ret);
+}
+
+static int lookup_id(sd_netlink *nl, sd_genl_family family, uint16_t *id) {
+ int r;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+
+ r = sd_genl_message_new(nl, SD_GENL_ID_CTRL, CTRL_CMD_GETFAMILY, &req);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_append_string(req, CTRL_ATTR_FAMILY_NAME, genl_families[family].name);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(nl, req, 0, &reply);
+ if (r < 0)
+ return r;
+
+ return sd_netlink_message_read_u16(reply, CTRL_ATTR_FAMILY_ID, id);
+}
diff --git a/src/libsystemd/sd-netlink/local-addresses.c b/src/libsystemd/sd-netlink/local-addresses.c
index 23acec4061..81e55b6d9f 100644
--- a/src/libsystemd/sd-netlink/local-addresses.c
+++ b/src/libsystemd/sd-netlink/local-addresses.c
@@ -225,6 +225,8 @@ int local_gateways(sd_netlink *context, int ifindex, int af, struct local_addres
continue;
r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi);
+ if (r == -ENODATA) /* Not all routes have an RTA_OIF attribute (for example nexthop ones) */
+ continue;
if (r < 0)
return r;
if (ifindex > 0 && (int) ifi != ifindex)
diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h
index f045ff67ca..dc553d708c 100644
--- a/src/libsystemd/sd-netlink/netlink-internal.h
+++ b/src/libsystemd/sd-netlink/netlink-internal.h
@@ -62,6 +62,8 @@ struct sd_netlink {
struct sockaddr_nl nl;
} sockaddr;
+ int protocol;
+
Hashmap *broadcast_group_refs;
bool broadcast_group_dont_leave:1; /* until we can rely on 4.2 */
@@ -111,6 +113,8 @@ struct sd_netlink_message {
sd_netlink *rtnl;
+ int protocol;
+
struct nlmsghdr *hdr;
struct netlink_container containers[RTNL_CONTAINER_DEPTH];
unsigned n_containers; /* number of containers */
@@ -123,6 +127,8 @@ struct sd_netlink_message {
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 netlink_open_family(sd_netlink **ret, int family);
+
int socket_open(int family);
int socket_bind(sd_netlink *nl);
int socket_broadcast_group_ref(sd_netlink *nl, unsigned group);
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index c88754540e..af3d13edcd 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -55,7 +55,7 @@ int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) {
return -ENOMEM;
m->n_ref = REFCNT_INIT;
-
+ m->protocol = rtnl->protocol;
m->sealed = false;
*ret = m;
@@ -66,10 +66,15 @@ int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) {
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;
+ const NLTypeSystem *type_system_root;
size_t size;
int r;
- r = type_system_get_type(&type_system_root, &nl_type, type);
+ assert_return(rtnl, -EINVAL);
+
+ type_system_root = type_system_get_root(rtnl->protocol);
+
+ r = type_system_get_type(type_system_root, &nl_type, type);
if (r < 0)
return r;
@@ -111,9 +116,10 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) {
}
sd_netlink_message *sd_netlink_message_ref(sd_netlink_message *m) {
- if (m)
- assert_se(REFCNT_INC(m->n_ref) >= 2);
+ if (!m)
+ return NULL;
+ assert_se(REFCNT_INC(m->n_ref) >= 2);
return m;
}
@@ -186,6 +192,10 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da
/* get the new message size (with padding at the end) */
message_length = offset + RTA_ALIGN(rta_length);
+ /* buffer should be smaller than both one page or 8K to be accepted by the kernel */
+ if (message_length > MIN(page_size(), 8192UL))
+ return -ENOBUFS;
+
/* realloc to fit the new attribute */
new_hdr = realloc(m->hdr, message_length);
if (!new_hdr)
@@ -490,7 +500,7 @@ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned shor
if (r < 0)
return r;
- /* do we evere need non-null size */
+ /* do we ever need non-null size */
r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
if (r < 0)
return r;
@@ -500,14 +510,53 @@ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned shor
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->containers[m->n_containers].offset = 0;
+ m->n_containers--;
+
+ return 0;
+}
+
+int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) {
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(m->n_containers > 0, -EINVAL);
+
+ r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
+ if (r < 0)
+ return r;
+
+ m->containers[m->n_containers].offset = r;
+ m->n_containers++;
+ m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
+
+ return 0;
+}
+
+int sd_netlink_message_cancel_array(sd_netlink_message *m) {
+ unsigned i;
+ uint32_t rta_len;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(m->n_containers > 1, -EINVAL);
+
+ rta_len = GET_CONTAINER(m, (m->n_containers - 1))->rta_len;
+
+ for (i = 0; i < m->n_containers; i++)
+ GET_CONTAINER(m, i)->rta_len -= rta_len;
+
+ m->hdr->nlmsg_len -= rta_len;
+
m->n_containers--;
+ m->containers[m->n_containers].type_system = NULL;
return 0;
}
@@ -519,13 +568,14 @@ static int netlink_message_read_internal(sd_netlink_message *m, unsigned short t
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)
+ if (attribute->offset == 0)
return -ENODATA;
rta = (struct rtattr*)((uint8_t *) m->hdr + attribute->offset);
@@ -745,7 +795,7 @@ static int netlink_container_parse(sd_netlink_message *m,
if (type >= count)
continue;
- if (attributes[type].offset)
+ if (attributes[type].offset != 0)
log_debug("rtnl: message parse - overwriting repeated attribute");
attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr;
@@ -899,6 +949,7 @@ int sd_netlink_message_get_errno(sd_netlink_message *m) {
int sd_netlink_message_rewind(sd_netlink_message *m) {
const NLType *nl_type;
+ const NLTypeSystem *type_system_root;
uint16_t type;
size_t size;
unsigned i;
@@ -910,6 +961,8 @@ int sd_netlink_message_rewind(sd_netlink_message *m) {
if (!m->sealed)
rtnl_message_seal(m);
+ type_system_root = type_system_get_root(m->protocol);
+
for (i = 1; i <= m->n_containers; i++)
m->containers[i].attributes = mfree(m->containers[i].attributes);
@@ -921,7 +974,7 @@ int sd_netlink_message_rewind(sd_netlink_message *m) {
assert(m->hdr);
- r = type_system_get_type(&type_system_root, &nl_type, m->hdr->nlmsg_type);
+ r = type_system_get_type(type_system_root, &nl_type, m->hdr->nlmsg_type);
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c
index 22be94382a..e08248c9f6 100644
--- a/src/libsystemd/sd-netlink/netlink-socket.c
+++ b/src/libsystemd/sd-netlink/netlink-socket.c
@@ -269,13 +269,13 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
};
struct cmsghdr *cmsg;
uint32_t group = 0;
- int r;
+ ssize_t n;
assert(fd >= 0);
assert(iov);
- r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
- if (r < 0) {
+ n = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
+ if (n < 0) {
/* no data */
if (errno == ENOBUFS)
log_debug("rtnl: kernel receive buffer overrun");
@@ -291,8 +291,8 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
if (peek) {
/* drop the message */
- r = recvmsg(fd, &msg, 0);
- if (r < 0)
+ n = recvmsg(fd, &msg, 0);
+ if (n < 0)
return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
}
@@ -313,7 +313,7 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
if (_group)
*_group = group;
- return r;
+ return (int) n;
}
/* On success, the number of bytes received is returned and *ret points to the received message
@@ -330,17 +330,20 @@ int socket_read_message(sd_netlink *rtnl) {
size_t len;
int r;
unsigned i = 0;
+ const NLTypeSystem *type_system_root;
assert(rtnl);
assert(rtnl->rbuffer);
assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
+ type_system_root = type_system_get_root(rtnl->protocol);
+
/* 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;
+ len = (size_t) r;
/* make room for the pending message */
if (!greedy_realloc((void **)&rtnl->rbuffer,
@@ -356,7 +359,7 @@ int socket_read_message(sd_netlink *rtnl) {
if (r <= 0)
return r;
else
- len = (size_t)r;
+ len = (size_t) r;
if (len > rtnl->rbuffer_allocated)
/* message did not fit in read buffer */
@@ -396,7 +399,7 @@ int socket_read_message(sd_netlink *rtnl) {
}
/* check that we support this message type */
- r = type_system_get_type(&type_system_root, &nl_type, new_msg->nlmsg_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",
@@ -433,7 +436,7 @@ int socket_read_message(sd_netlink *rtnl) {
m = NULL;
}
- if (len)
+ if (len > 0)
log_debug("sd-netlink: discarding %zu bytes of incoming message", len);
if (!first)
@@ -459,9 +462,9 @@ int socket_read_message(sd_netlink *rtnl) {
} else {
/* we only got a partial multi-part message, push it on the
partial read queue */
- if (i < rtnl->rqueue_partial_size) {
+ if (i < rtnl->rqueue_partial_size)
rtnl->rqueue_partial[i] = first;
- } else {
+ else {
r = rtnl_rqueue_partial_make_room(rtnl);
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index f8be296d37..0ee7d6f0dc 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -18,25 +18,22 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <netinet/in.h>
#include <stdint.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+#include <linux/genetlink.h>
+#include <linux/ip.h>
+#include <linux/if.h>
#include <linux/can/netlink.h>
#include <linux/fib_rules.h>
-#include <linux/in6.h>
-#include <linux/veth.h>
-#include <linux/if_bridge.h>
#include <linux/if_addr.h>
#include <linux/if_addrlabel.h>
-#include <linux/if.h>
-#include <linux/ip.h>
-#include <linux/if_addr.h>
#include <linux/if_bridge.h>
#include <linux/if_link.h>
#include <linux/if_tunnel.h>
-#include <linux/fib_rules.h>
-
+#include <linux/veth.h>
#if HAVE_VXCAN_INFO_PEER
#include <linux/can/vxcan.h>
#endif
@@ -44,8 +41,10 @@
#include "macro.h"
#include "missing.h"
#include "netlink-types.h"
+#include "sd-netlink.h"
#include "string-table.h"
#include "util.h"
+#include "wireguard-netlink.h"
/* Maximum ARP IP target defined in kernel */
#define BOND_MAX_ARP_TARGETS 16
@@ -102,6 +101,7 @@ static const NLType rtnl_link_info_data_vxcan_types[] = {
static const NLType rtnl_link_info_data_ipvlan_types[] = {
[IFLA_IPVLAN_MODE] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_IPVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 },
};
static const NLType rtnl_link_info_data_macvlan_types[] = {
@@ -337,7 +337,7 @@ static const char* const nl_union_link_info_data_table[] = {
[NL_UNION_LINK_INFO_DATA_VCAN] = "vcan",
[NL_UNION_LINK_INFO_DATA_GENEVE] = "geneve",
[NL_UNION_LINK_INFO_DATA_VXCAN] = "vxcan",
-
+ [NL_UNION_LINK_INFO_DATA_WIREGUARD] = "wireguard",
};
DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
@@ -552,6 +552,8 @@ static const NLType rtnl_route_metrics_types[] = {
[RTAX_INITCWND] = { .type = NETLINK_TYPE_U32 },
[RTAX_FEATURES] = { .type = NETLINK_TYPE_U32 },
[RTAX_RTO_MIN] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_INITRWND] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_QUICKACK] = { .type = NETLINK_TYPE_U32 },
};
static const NLTypeSystem rtnl_route_metrics_type_system = {
@@ -663,11 +665,98 @@ static const NLType rtnl_types[] = {
[RTM_GETRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
};
-const NLTypeSystem type_system_root = {
+const NLTypeSystem rtnl_type_system_root = {
.count = ELEMENTSOF(rtnl_types),
.types = rtnl_types,
};
+static const NLType genl_wireguard_allowedip_types[] = {
+ [WGALLOWEDIP_A_FAMILY] = { .type = NETLINK_TYPE_U16 },
+ [WGALLOWEDIP_A_IPADDR] = { .type = NETLINK_TYPE_IN_ADDR },
+ [WGALLOWEDIP_A_CIDR_MASK] = { .type = NETLINK_TYPE_U8 },
+};
+
+static const NLTypeSystem genl_wireguard_allowedip_type_system = {
+ .count = ELEMENTSOF(genl_wireguard_allowedip_types),
+ .types = genl_wireguard_allowedip_types,
+};
+
+static const NLType genl_wireguard_peer_types[] = {
+ [WGPEER_A_PUBLIC_KEY] = { .size = WG_KEY_LEN },
+ [WGPEER_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [WGPEER_A_PRESHARED_KEY] = { .size = WG_KEY_LEN },
+ [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NETLINK_TYPE_U32 },
+ [WGPEER_A_ENDPOINT] = { /* either size of sockaddr_in or sockaddr_in6 depending on address family */ },
+ [WGPEER_A_ALLOWEDIPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_allowedip_type_system },
+};
+
+static const NLTypeSystem genl_wireguard_peer_type_system = {
+ .count = ELEMENTSOF(genl_wireguard_peer_types),
+ .types = genl_wireguard_peer_types,
+};
+
+static const NLType genl_wireguard_set_device_types[] = {
+ [WGDEVICE_A_IFINDEX] = { .type = NETLINK_TYPE_U32 },
+ [WGDEVICE_A_IFNAME] = { .type = NETLINK_TYPE_STRING },
+ [WGDEVICE_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [WGDEVICE_A_PRIVATE_KEY] = { .size = WG_KEY_LEN },
+ [WGDEVICE_A_LISTEN_PORT] = { .type = NETLINK_TYPE_U16 },
+ [WGDEVICE_A_FWMARK] = { .type = NETLINK_TYPE_U32 },
+ [WGDEVICE_A_PEERS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_peer_type_system },
+};
+
+static const NLTypeSystem genl_wireguard_set_device_type_system = {
+ .count = ELEMENTSOF(genl_wireguard_set_device_types),
+ .types = genl_wireguard_set_device_types,
+};
+
+static const NLType genl_wireguard_cmds[] = {
+ [WG_CMD_SET_DEVICE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_set_device_type_system },
+};
+
+static const NLTypeSystem genl_wireguard_type_system = {
+ .count = ELEMENTSOF(genl_wireguard_cmds),
+ .types = genl_wireguard_cmds,
+};
+
+static const NLType genl_get_family_types[] = {
+ [CTRL_ATTR_FAMILY_NAME] = { .type = NETLINK_TYPE_STRING },
+ [CTRL_ATTR_FAMILY_ID] = { .type = NETLINK_TYPE_U16 },
+};
+
+static const NLTypeSystem genl_get_family_type_system = {
+ .count = ELEMENTSOF(genl_get_family_types),
+ .types = genl_get_family_types,
+};
+
+static const NLType genl_ctrl_id_ctrl_cmds[] = {
+ [CTRL_CMD_GETFAMILY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system },
+};
+
+static const NLTypeSystem genl_ctrl_id_ctrl_type_system = {
+ .count = ELEMENTSOF(genl_ctrl_id_ctrl_cmds),
+ .types = genl_ctrl_id_ctrl_cmds,
+};
+
+static const NLType genl_families[] = {
+ [SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
+ [SD_GENL_WIREGUARD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_type_system },
+};
+
+const NLTypeSystem genl_family_type_system_root = {
+ .count = ELEMENTSOF(genl_families),
+ .types = genl_families,
+};
+
+static const NLType genl_types[] = {
+ [GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system, .size = sizeof(struct genlmsghdr) },
+};
+
+const NLTypeSystem genl_type_system_root = {
+ .count = ELEMENTSOF(genl_types),
+ .types = genl_types,
+};
+
uint16_t type_get_type(const NLType *type) {
assert(type);
return type->type;
@@ -701,6 +790,15 @@ uint16_t type_system_get_count(const NLTypeSystem *type_system) {
return type_system->count;
}
+const NLTypeSystem *type_system_get_root(int protocol) {
+ switch (protocol) {
+ case NETLINK_GENERIC:
+ return &genl_type_system_root;
+ default: /* NETLINK_ROUTE: */
+ return &rtnl_type_system_root;
+ }
+}
+
int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type) {
const NLType *nl_type;
diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h
index 57b46339f9..ea7f8d5e6c 100644
--- a/src/libsystemd/sd-netlink/netlink-types.h
+++ b/src/libsystemd/sd-netlink/netlink-types.h
@@ -54,13 +54,16 @@ struct NLTypeSystemUnion {
const NLTypeSystem *type_systems;
};
-extern const NLTypeSystem type_system_root;
+extern const NLTypeSystem rtnl_type_system_root;
+extern const NLTypeSystem genl_type_system_root;
+extern const NLTypeSystem genl_family_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);
+const NLTypeSystem* type_system_get_root(int protocol);
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);
@@ -91,6 +94,7 @@ typedef enum NLUnionLinkInfoData {
NL_UNION_LINK_INFO_DATA_VCAN,
NL_UNION_LINK_INFO_DATA_GENEVE,
NL_UNION_LINK_INFO_DATA_VXCAN,
+ NL_UNION_LINK_INFO_DATA_WIREGUARD,
_NL_UNION_LINK_INFO_DATA_MAX,
_NL_UNION_LINK_INFO_DATA_INVALID = -1
} NLUnionLinkInfoData;
diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c
index b32fad271a..7680b30e70 100644
--- a/src/libsystemd/sd-netlink/netlink-util.c
+++ b/src/libsystemd/sd-netlink/netlink-util.c
@@ -98,13 +98,13 @@ int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias,
return 0;
}
-int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret) {
+int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) {
struct nlmsgerr *err;
int r;
assert(error <= 0);
- r = message_new(NULL, ret, NLMSG_ERROR);
+ r = message_new(rtnl, ret, NLMSG_ERROR);
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h
index c804f5514c..795e4dc15c 100644
--- a/src/libsystemd/sd-netlink/netlink-util.h
+++ b/src/libsystemd/sd-netlink/netlink-util.h
@@ -24,7 +24,7 @@
#include "util.h"
-int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret);
+int rtnl_message_new_synthetic_error(sd_netlink *rtnl, 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);
diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c
index 924b0c954f..116e287bb6 100644
--- a/src/libsystemd/sd-netlink/sd-netlink.c
+++ b/src/libsystemd/sd-netlink/sd-netlink.c
@@ -30,6 +30,7 @@
#include "missing.h"
#include "netlink-internal.h"
#include "netlink-util.h"
+#include "process-util.h"
#include "socket-util.h"
#include "util.h"
@@ -46,6 +47,7 @@ static int sd_netlink_new(sd_netlink **ret) {
rtnl->fd = -1;
rtnl->sockaddr.nl.nl_family = AF_NETLINK;
rtnl->original_pid = getpid_cached();
+ rtnl->protocol = -1;
LIST_HEAD_INIT(rtnl->match_callbacks);
@@ -106,6 +108,8 @@ static bool rtnl_pid_changed(sd_netlink *rtnl) {
int sd_netlink_open_fd(sd_netlink **ret, int fd) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int r;
+ int protocol;
+ socklen_t l;
assert_return(ret, -EINVAL);
assert_return(fd >= 0, -EBADF);
@@ -114,11 +118,18 @@ int sd_netlink_open_fd(sd_netlink **ret, int fd) {
if (r < 0)
return r;
+ l = sizeof(protocol);
+ r = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &l);
+ if (r < 0)
+ return r;
+
rtnl->fd = fd;
+ rtnl->protocol = protocol;
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 */
+ rtnl->protocol = -1;
return r;
}
@@ -128,11 +139,11 @@ int sd_netlink_open_fd(sd_netlink **ret, int fd) {
return 0;
}
-int sd_netlink_open(sd_netlink **ret) {
+int netlink_open_family(sd_netlink **ret, int family) {
_cleanup_close_ int fd = -1;
int r;
- fd = socket_open(NETLINK_ROUTE);
+ fd = socket_open(family);
if (fd < 0)
return fd;
@@ -145,6 +156,10 @@ int sd_netlink_open(sd_netlink **ret) {
return 0;
}
+int sd_netlink_open(sd_netlink **ret) {
+ return netlink_open_family(ret, NETLINK_ROUTE);
+}
+
int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) {
assert_return(rtnl, -EINVAL);
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
@@ -309,7 +324,7 @@ static int process_timeout(sd_netlink *rtnl) {
if (c->timeout > n)
return 0;
- r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
+ r = rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, c->serial, &m);
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-netlink/test-local-addresses.c b/src/libsystemd/sd-netlink/test-local-addresses.c
index bb195b9de9..bf11042221 100644
--- a/src/libsystemd/sd-netlink/test-local-addresses.c
+++ b/src/libsystemd/sd-netlink/test-local-addresses.c
@@ -38,6 +38,10 @@ int main(int argc, char *argv[]) {
struct local_address *a;
int n;
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
a = NULL;
n = local_addresses(NULL, 0, AF_UNSPEC, &a);
assert_se(n >= 0);
diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c
index 73e5af0060..9ccc8ea607 100644
--- a/src/libsystemd/sd-netlink/test-netlink.c
+++ b/src/libsystemd/sd-netlink/test-netlink.c
@@ -143,13 +143,13 @@ static void test_address_get(sd_netlink *rtnl, int ifindex) {
}
-static void test_route(void) {
+static void test_route(sd_netlink *rtnl) {
_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);
+ r = sd_rtnl_message_new_route(rtnl, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC);
if (r < 0) {
log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
return;
@@ -291,13 +291,13 @@ static void test_pipe(int ifindex) {
assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
}
-static void test_container(void) {
+static void test_container(sd_netlink *rtnl) {
_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_rtnl_message_new_link(rtnl, &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);
@@ -369,10 +369,10 @@ static void test_get_addresses(sd_netlink *rtnl) {
}
}
-static void test_message(void) {
+static void test_message(sd_netlink *rtnl) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- assert_se(rtnl_message_new_synthetic_error(-ETIMEDOUT, 1, &m) >= 0);
+ assert_se(rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, 1, &m) >= 0);
assert_se(sd_netlink_message_get_errno(m) == -ETIMEDOUT);
}
@@ -384,19 +384,19 @@ int main(void) {
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);
+ test_route(rtnl);
+
+ test_message(rtnl);
+
+ test_container(rtnl);
+
if_loopback = (int) if_nametoindex("lo");
assert_se(if_loopback > 0);
diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c
index be3748e3ce..787642a7fb 100644
--- a/src/libsystemd/sd-resolve/sd-resolve.c
+++ b/src/libsystemd/sd-resolve/sd-resolve.c
@@ -40,6 +40,7 @@
#include "missing.h"
#include "socket-util.h"
#include "util.h"
+#include "process-util.h"
#define WORKERS_MIN 1U
#define WORKERS_MAX 16U
@@ -398,14 +399,9 @@ static int handle_request(int out_fd, const Packet *packet, size_t length) {
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");
+ (void) pthread_setname_np(pthread_self(), "sd-resolve");
while (!resolve->dead) {
union {
@@ -437,8 +433,18 @@ static void* thread_worker(void *p) {
}
static int start_threads(sd_resolve *resolve, unsigned extra) {
+ sigset_t ss, saved_ss;
unsigned n;
- int r;
+ int r, k;
+
+ if (sigfillset(&ss) < 0)
+ return -errno;
+
+ /* No signals in forked off threads please. We set the mask before forking, so that the threads never exist
+ * with a different mask than a fully blocked one */
+ r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss);
+ if (r > 0)
+ return -r;
n = resolve->n_outstanding + extra;
n = CLAMP(n, WORKERS_MIN, WORKERS_MAX);
@@ -446,13 +452,22 @@ static int start_threads(sd_resolve *resolve, unsigned extra) {
while (resolve->n_valid_workers < n) {
r = pthread_create(&resolve->workers[resolve->n_valid_workers], NULL, thread_worker, resolve);
- if (r != 0)
- return -r;
+ if (r > 0) {
+ r = -r;
+ goto finish;
+ }
resolve->n_valid_workers++;
}
- return 0;
+ r = 0;
+
+finish:
+ k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL);
+ if (k > 0 && r >= 0)
+ r = -k;
+
+ return r;
}
static bool resolve_pid_changed(sd_resolve *r) {
diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c
index 752eb15228..b728dee9dd 100644
--- a/src/libsystemd/sd-resolve/test-resolve.c
+++ b/src/libsystemd/sd-resolve/test-resolve.c
@@ -89,7 +89,9 @@ int main(int argc, char *argv[]) {
assert_se(sd_resolve_default(&resolve) >= 0);
/* Test a floating resolver query */
- sd_resolve_getaddrinfo(resolve, NULL, "redhat.com", "http", NULL, getaddrinfo_handler, NULL);
+ r = sd_resolve_getaddrinfo(resolve, NULL, "redhat.com", "http", NULL, getaddrinfo_handler, NULL);
+ if (r < 0)
+ log_error_errno(r, "sd_resolve_getaddrinfo(): %m");
/* Make a name -> address query */
r = sd_resolve_getaddrinfo(resolve, &q1, argc >= 2 ? argv[1] : "www.heise.de", NULL, &hints, getaddrinfo_handler, NULL);
diff --git a/src/libudev/libudev-queue.c b/src/libudev/libudev-queue.c
index b941afb773..85ceb263a3 100644
--- a/src/libudev/libudev-queue.c
+++ b/src/libudev/libudev-queue.c
@@ -268,8 +268,16 @@ _public_ int udev_queue_get_fd(struct udev_queue *udev_queue) {
* Returns: the result of clearing the watch for queue changes.
*/
_public_ int udev_queue_flush(struct udev_queue *udev_queue) {
+ int r;
+
+ assert(udev_queue);
+
if (udev_queue->fd < 0)
return -EINVAL;
- return flush_fd(udev_queue->fd);
+ r = flush_fd(udev_queue->fd);
+ if (r < 0)
+ return r;
+
+ return 0;
}
diff --git a/src/libudev/meson.build b/src/libudev/meson.build
index 30d6721b60..c381352ce8 100644
--- a/src/libudev/meson.build
+++ b/src/libudev/meson.build
@@ -31,21 +31,8 @@ libudev_sources = files('''
############################################################
-libudev_sym = 'libudev.sym'
-libudev_sym_path = '@0@/@1@'.format(meson.current_source_dir(), libudev_sym)
-libudev = shared_library(
- 'udev',
- libudev_sources,
- version : libudev_version,
- include_directories : includes,
- link_args : ['-shared',
- '-Wl,--version-script=' + libudev_sym_path],
- link_with : [libbasic,
- libsystemd_internal],
- dependencies : [threads],
- link_depends : libudev_sym,
- install : true,
- install_dir : rootlibdir)
+libudev_sym = files('libudev.sym')
+libudev_sym_path = meson.current_source_dir() + '/libudev.sym'
install_headers('libudev.h')
libudev_h_path = '@0@/libudev.h'.format(meson.current_source_dir())
diff --git a/src/locale/localectl.c b/src/locale/localectl.c
index f09fe42626..af39e431f5 100644
--- a/src/locale/localectl.c
+++ b/src/locale/localectl.c
@@ -595,7 +595,7 @@ static int localectl_main(sd_bus *bus, int argc, char *argv[]) {
}
int main(int argc, char*argv[]) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -615,6 +615,9 @@ int main(int argc, char*argv[]) {
r = localectl_main(bus, argc, argv);
finish:
+ /* make sure we terminate the bus connection first, and then close the
+ * pager, see issue #3543 for the details. */
+ sd_bus_flush_close_unref(bus);
pager_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/locale/localed.c b/src/locale/localed.c
index 3e3f03e046..02f5e8c656 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -652,9 +652,9 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
if (r < 0)
return log_error_errno(r, "Failed to register object: %m");
- r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
+ r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.locale1", 0, NULL, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to register name: %m");
+ return log_error_errno(r, "Failed to request name: %m");
r = sd_bus_attach_event(bus, event, 0);
if (r < 0)
diff --git a/src/login/73-seat-late.rules.in b/src/login/73-seat-late.rules.m4
index d2546c8ee9..4db8d4dd4c 100644
--- a/src/login/73-seat-late.rules.in
+++ b/src/login/73-seat-late.rules.m4
@@ -13,7 +13,8 @@ ENV{ID_SEAT}=="", ENV{ID_AUTOSEAT}=="1", ENV{ID_FOR_SEAT}!="", ENV{ID_SEAT}="sea
ENV{ID_SEAT}=="", IMPORT{parent}="ID_SEAT"
ENV{ID_SEAT}!="", TAG+="$env{ID_SEAT}"
-
-TAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess"
+m4_ifdef(`HAVE_ACL',``
+TAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess"''
+)m4_dnl
LABEL="seat_late_end"
diff --git a/src/login/inhibit.c b/src/login/inhibit.c
index 7b9e3f0f6e..22657f9eda 100644
--- a/src/login/inhibit.c
+++ b/src/login/inhibit.c
@@ -266,26 +266,17 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- pid = fork();
- if (pid < 0) {
- log_error_errno(errno, "Failed to fork: %m");
+ r = safe_fork("(inhibit)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ if (r < 0)
return EXIT_FAILURE;
- }
-
- if (pid == 0) {
+ if (r == 0) {
/* Child */
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- close_all_fds(NULL, 0);
-
execvp(argv[optind], argv + optind);
log_error_errno(errno, "Failed to execute %s: %m", argv[optind]);
_exit(EXIT_FAILURE);
}
- r = wait_for_terminate_and_warn(argv[optind], pid, true);
+ r = wait_for_terminate_and_check(argv[optind], pid, WAIT_LOG);
return r < 0 ? EXIT_FAILURE : r;
}
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index dfcaff6195..c811ee6c5e 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -1584,7 +1584,7 @@ static int loginctl_main(int argc, char *argv[], sd_bus *bus) {
}
int main(int argc, char *argv[]) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -1607,6 +1607,9 @@ int main(int argc, char *argv[]) {
r = loginctl_main(argc, argv, bus);
finish:
+ /* make sure we terminate the bus connection first, and then close the
+ * pager, see issue #3543 for the details. */
+ sd_bus_flush_close_unref(bus);
pager_close();
polkit_agent_close();
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
index adeba746f5..e338682f41 100644
--- a/src/login/logind-core.c
+++ b/src/login/logind-core.c
@@ -31,6 +31,7 @@
#include "fd-util.h"
#include "logind.h"
#include "parse-util.h"
+#include "process-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "udev-util.h"
diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c
index 8a6487ea45..e14835292e 100644
--- a/src/login/logind-inhibit.c
+++ b/src/login/logind-inhibit.c
@@ -305,7 +305,7 @@ int inhibitor_create_fifo(Inhibitor *i) {
/* Open reading side */
if (i->fifo_fd < 0) {
- i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
+ i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (i->fifo_fd < 0)
return -errno;
}
@@ -321,7 +321,7 @@ int inhibitor_create_fifo(Inhibitor *i) {
}
/* Open writing side */
- r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
+ r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
if (r < 0)
return -errno;
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index c4bde80c0c..92eb2943fe 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -962,7 +962,7 @@ int session_create_fifo(Session *s) {
/* Open reading side */
if (s->fifo_fd < 0) {
- s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
+ s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (s->fifo_fd < 0)
return -errno;
@@ -981,7 +981,7 @@ int session_create_fifo(Session *s) {
}
/* Open writing side */
- r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
+ r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
if (r < 0)
return -errno;
diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c
index 9fca5ce0cd..d5d086cfe0 100644
--- a/src/login/logind-user-dbus.c
+++ b/src/login/logind-user-dbus.c
@@ -288,13 +288,13 @@ int user_object_find(sd_bus *bus, const char *path, const char *interface, void
return 0;
r = parse_uid(p, &uid);
- }
- if (r < 0)
- return 0;
+ if (r < 0)
+ return 0;
- user = hashmap_get(m->users, UID_TO_PTR(uid));
- if (!user)
- return 0;
+ user = hashmap_get(m->users, UID_TO_PTR(uid));
+ if (!user)
+ return 0;
+ }
*found = user;
return 1;
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index 94e250b94a..32b2045696 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -344,16 +344,13 @@ static int user_mkdir_runtime_path(User *u) {
if (path_is_mount_point(u->runtime_path, NULL, 0) <= 0) {
_cleanup_free_ char *t = NULL;
- (void) mkdir_label(u->runtime_path, 0700);
+ r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu%s",
+ u->uid, u->gid, u->manager->runtime_dir_size,
+ mac_smack_use() ? ",smackfsroot=*" : "");
+ if (r < 0)
+ return log_oom();
- if (mac_smack_use())
- r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
- else
- r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
- if (r < 0) {
- r = log_oom();
- goto fail;
- }
+ (void) mkdir_label(u->runtime_path, 0700);
r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t);
if (r < 0) {
@@ -461,7 +458,7 @@ int user_start(User *u) {
u->stopping = false;
if (!u->started) {
- log_debug("New user %s logged in.", u->name);
+ log_debug("Starting services for new user %s.", u->name);
/* Make XDG_RUNTIME_DIR */
r = user_mkdir_runtime_path(u);
@@ -530,9 +527,7 @@ static int user_stop_service(User *u) {
return r;
}
- free(u->service_job);
- u->service_job = job;
-
+ free_and_replace(u->service_job, job);
return r;
}
diff --git a/src/login/logind.c b/src/login/logind.c
index 49ca367e18..d15d4cec5b 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -29,17 +29,18 @@
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
+#include "cgroup-util.h"
#include "conf-parser.h"
#include "def.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "logind.h"
+#include "process-util.h"
#include "selinux-util.h"
#include "signal-util.h"
#include "strv.h"
#include "udev-util.h"
-#include "cgroup-util.h"
static void manager_free(Manager *m);
@@ -658,7 +659,6 @@ static int manager_reserve_vt(Manager *m) {
}
static int manager_connect_bus(Manager *m) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(m);
@@ -696,65 +696,65 @@ static int manager_connect_bus(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to add user enumerator: %m");
- r = sd_bus_add_match(m->bus,
- NULL,
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='JobRemoved',"
- "path='/org/freedesktop/systemd1'",
- match_job_removed, m);
+ r = sd_bus_match_signal_async(
+ m->bus,
+ NULL,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "JobRemoved",
+ match_job_removed, NULL, m);
if (r < 0)
- return log_error_errno(r, "Failed to add match for JobRemoved: %m");
-
- r = sd_bus_add_match(m->bus,
- NULL,
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='UnitRemoved',"
- "path='/org/freedesktop/systemd1'",
- match_unit_removed, m);
+ return log_error_errno(r, "Failed to request match for JobRemoved: %m");
+
+ r = sd_bus_match_signal_async(
+ m->bus,
+ NULL,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "UnitRemoved",
+ match_unit_removed, NULL, m);
if (r < 0)
- return log_error_errno(r, "Failed to add match for UnitRemoved: %m");
-
- r = sd_bus_add_match(m->bus,
- NULL,
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.DBus.Properties',"
- "member='PropertiesChanged'",
- match_properties_changed, m);
+ return log_error_errno(r, "Failed to request match for UnitRemoved: %m");
+
+ r = sd_bus_match_signal_async(
+ m->bus,
+ NULL,
+ "org.freedesktop.systemd1",
+ NULL,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ match_properties_changed, NULL, m);
if (r < 0)
- return log_error_errno(r, "Failed to add match for PropertiesChanged: %m");
-
- r = sd_bus_add_match(m->bus,
- NULL,
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='Reloading',"
- "path='/org/freedesktop/systemd1'",
- match_reloading, m);
+ return log_error_errno(r, "Failed to request match for PropertiesChanged: %m");
+
+ r = sd_bus_match_signal_async(
+ m->bus,
+ NULL,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "Reloading",
+ match_reloading, NULL, m);
if (r < 0)
- return log_error_errno(r, "Failed to add match for Reloading: %m");
+ return log_error_errno(r, "Failed to request match for Reloading: %m");
- r = sd_bus_call_method(
+ r = sd_bus_call_method_async(
m->bus,
+ NULL,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"Subscribe",
- &error,
- NULL, NULL);
- if (r < 0) {
- log_error("Failed to enable subscription: %s", bus_error_message(&error, r));
- return r;
- }
+ NULL, NULL,
+ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable subscription: %m");
- r = sd_bus_request_name(m->bus, "org.freedesktop.login1", 0);
+ r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.login1", 0, NULL, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to register name: %m");
+ return log_error_errno(r, "Failed to request name: %m");
r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
diff --git a/src/login/meson.build b/src/login/meson.build
index 33f9ed48cc..e8e4f7bd7d 100644
--- a/src/login/meson.build
+++ b/src/login/meson.build
@@ -97,19 +97,27 @@ if conf.get('ENABLE_LOGIND') == 1
install : install_polkit,
install_dir : polkitpolicydir)
- install_data('70-power-switch.rules',
- '70-uaccess.rules',
+ install_data('70-power-switch.rules', install_dir : udevrulesdir)
+
+ if conf.get('HAVE_ACL') == 1
+ install_data('70-uaccess.rules', install_dir : udevrulesdir)
+ endif
+
+ seat_rules = configure_file(
+ input : '71-seat.rules.in',
+ output : '71-seat.rules',
+ configuration : substs)
+ install_data(seat_rules,
install_dir : udevrulesdir)
- foreach file : ['71-seat.rules',
- '73-seat-late.rules']
- gen = configure_file(
- input : file + '.in',
- output : file,
- configuration : substs)
- install_data(gen,
- install_dir : udevrulesdir)
- endforeach
+ custom_target(
+ '73-seat-late.rules',
+ input : '73-seat-late.rules.m4',
+ output: '73-seat-late.rules',
+ command : [m4, '-P'] + m4_defines + ['@INPUT@'],
+ capture : true,
+ install : true,
+ install_dir : udevrulesdir)
custom_target(
'systemd-user',
diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c
index 246bbddeee..1c3ba33e23 100644
--- a/src/login/pam_systemd.c
+++ b/src/login/pam_systemd.c
@@ -197,7 +197,7 @@ static int export_legacy_dbus_address(
return PAM_SUCCESS;
s = mfree(s);
- if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, runtime) < 0)
+ if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0)
goto error;
r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0);
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index 10d1b06016..8ba1380c81 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -75,10 +75,10 @@ int bus_image_method_remove(
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
- child = fork();
- if (child < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
- if (child == 0) {
+ r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
+ if (r == 0) {
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
r = image_remove(image);
@@ -187,10 +187,10 @@ int bus_image_method_clone(
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
- child = fork();
- if (child < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
- if (child == 0) {
+ r = safe_fork("(imgclone)", FORK_RESET_SIGNALS, &child);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
+ if (r == 0) {
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
r = image_clone(image, new_name, read_only);
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index 3761267b57..2d7806491b 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -228,7 +228,6 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
_cleanup_free_ char *us = NULL, *them = NULL;
_cleanup_close_ int netns_fd = -1;
const char *p;
- siginfo_t si;
pid_t child;
r = readlink_malloc("/proc/self/ns/net", &us);
@@ -250,11 +249,10 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
return -errno;
- child = fork();
- if (child < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
-
- if (child == 0) {
+ r = safe_fork("(sd-addr)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
+ if (r == 0) {
_cleanup_free_ struct local_address *addresses = NULL;
struct local_address *a;
int i, n;
@@ -338,10 +336,10 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
return r;
}
- r = wait_for_terminate(child, &si);
+ r = wait_for_terminate_and_check("(sd-addr)", child, 0);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ if (r != EXIT_SUCCESS)
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
break;
}
@@ -380,7 +378,6 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
_cleanup_close_ int mntns_fd = -1, root_fd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
_cleanup_fclose_ FILE *f = NULL;
- siginfo_t si;
pid_t child;
r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
@@ -390,11 +387,10 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
return -errno;
- child = fork();
- if (child < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
-
- if (child == 0) {
+ r = safe_fork("(sd-osrel)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
+ if (r == 0) {
int fd = -1;
pair[0] = safe_close(pair[0]);
@@ -431,12 +427,12 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- r = wait_for_terminate(child, &si);
+ r = wait_for_terminate_and_check("(sd-osrel)", child, 0);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
- if (si.si_code == CLD_EXITED && si.si_status == EXIT_NOT_FOUND)
+ if (r == EXIT_NOT_FOUND)
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information");
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ if (r != EXIT_SUCCESS)
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
break;
@@ -613,7 +609,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
sd_bus *container_bus = NULL;
_cleanup_close_ int master = -1, slave = -1;
- _cleanup_strv_free_ char **env = NULL, **args = NULL;
+ _cleanup_strv_free_ char **env = NULL, **args_wire = NULL, **args = NULL;
Machine *m = userdata;
const char *p, *unit, *user, *path, *description, *utmp_id;
int r;
@@ -625,22 +621,41 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
if (r < 0)
return r;
user = empty_to_null(user);
- if (isempty(path))
- path = "/bin/sh";
- if (!path_is_absolute(path))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path);
-
- r = sd_bus_message_read_strv(message, &args);
+ r = sd_bus_message_read_strv(message, &args_wire);
if (r < 0)
return r;
- if (strv_isempty(args)) {
- args = strv_free(args);
+ if (isempty(path)) {
+ path = "/bin/sh";
- args = strv_new(path, NULL);
+ args = new0(char*, 3 + 1);
if (!args)
return -ENOMEM;
-
- args[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */
+ args[0] = strdup("sh");
+ if (!args[0])
+ return -ENOMEM;
+ args[1] = strdup("-c");
+ if (!args[1])
+ return -ENOMEM;
+ r = asprintf(&args[2],
+ "shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
+ "exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
+ isempty(user) ? "root" : user);
+ if (r < 0) {
+ args[2] = NULL;
+ return -ENOMEM;
+ }
+ } else {
+ if (!path_is_absolute(path))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path);
+ args = args_wire;
+ args_wire = NULL;
+ if (strv_isempty(args)) {
+ args = strv_free(args);
+
+ args = strv_new(path, NULL);
+ if (!args)
+ return -ENOMEM;
+ }
}
r = sd_bus_message_read_strv(message, &env);
@@ -842,7 +857,6 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
const char *dest, *src;
Machine *m = userdata;
struct stat st;
- siginfo_t si;
pid_t child;
uid_t uid;
int r;
@@ -931,7 +945,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
/* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */
mount_tmp = strjoina(mount_slave, "/mount");
if (S_ISDIR(st.st_mode))
- r = mkdir(mount_tmp, 0700) < 0 ? -errno : 0;
+ r = mkdir_errno_wrapper(mount_tmp, 0700);
else
r = touch(mount_tmp);
if (r < 0) {
@@ -997,13 +1011,12 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
goto finish;
}
- child = fork();
- if (child < 0) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+ r = safe_fork("(sd-bindmnt)", FORK_RESET_SIGNALS, &child);
+ if (r < 0) {
+ sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
goto finish;
}
-
- if (child == 0) {
+ if (r == 0) {
const char *mount_inside;
int mntfd;
const char *q;
@@ -1049,17 +1062,12 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
- r = wait_for_terminate(child, &si);
+ r = wait_for_terminate_and_check("(sd-bindmnt)", child, 0);
if (r < 0) {
r = sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
goto finish;
}
- if (si.si_code != CLD_EXITED) {
- r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
- goto finish;
- }
- if (si.si_status != EXIT_SUCCESS) {
-
+ if (r != EXIT_SUCCESS) {
if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r))
r = sd_bus_error_set_errnof(error, r, "Failed to mount: %m");
else
@@ -1172,11 +1180,10 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
- child = fork();
- if (child < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
-
- if (child == 0) {
+ r = safe_fork("(sd-copy)", FORK_RESET_SIGNALS, &child);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
+ if (r == 0) {
int containerfd;
const char *q;
int mntfd;
@@ -1272,7 +1279,6 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
case MACHINE_CONTAINER: {
_cleanup_close_ int mntns_fd = -1, root_fd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
- siginfo_t si;
pid_t child;
r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
@@ -1282,11 +1288,10 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- child = fork();
- if (child < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
-
- if (child == 0) {
+ r = safe_fork("(sd-openroot)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
+ if (r == 0) {
_cleanup_close_ int dfd = -1;
pair[0] = safe_close(pair[0]);
@@ -1309,10 +1314,10 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate(child, &si);
+ r = wait_for_terminate_and_check("(sd-openroot)", child, 0);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ if (r != EXIT_SUCCESS)
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
fd = receive_one_fd(pair[0], MSG_DONTWAIT);
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index c435bb9b5a..75743ce6a6 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -1568,9 +1568,9 @@ static int login_machine(int argc, char *argv[], void *userdata) {
"member='MachineRemoved',"
"arg0='", machine, "'");
- r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
+ r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward);
if (r < 0)
- return log_error_errno(r, "Failed to add machine removal match: %m");
+ return log_error_errno(r, "Failed to request machine removal match: %m");
r = sd_bus_call_method(
bus,
@@ -1643,9 +1643,9 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
"member='MachineRemoved',"
"arg0='", machine, "'");
- r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
+ r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward);
if (r < 0)
- return log_error_errno(r, "Failed to add machine removal match: %m");
+ return log_error_errno(r, "Failed to request machine removal match: %m");
r = sd_bus_message_new_method_call(
bus,
@@ -2087,28 +2087,27 @@ static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
- r = sd_bus_add_match(
+ r = sd_bus_match_signal_async(
bus,
&slot_job_removed,
- "type='signal',"
- "sender='org.freedesktop.import1',"
- "interface='org.freedesktop.import1.Manager',"
- "member='TransferRemoved',"
- "path='/org/freedesktop/import1'",
- match_transfer_removed, &path);
+ "org.freedesktop.import1",
+ "/org/freedesktop/import1",
+ "org.freedesktop.import1.Manager",
+ "TransferRemoved",
+ match_transfer_removed, NULL, &path);
if (r < 0)
- return log_error_errno(r, "Failed to install match: %m");
+ return log_error_errno(r, "Failed to request match: %m");
- r = sd_bus_add_match(
+ r = sd_bus_match_signal_async(
bus,
&slot_log_message,
- "type='signal',"
- "sender='org.freedesktop.import1',"
- "interface='org.freedesktop.import1.Transfer',"
- "member='LogMessage'",
- match_log_message, &path);
+ "org.freedesktop.import1",
+ NULL,
+ "org.freedesktop.import1.Transfer",
+ "LogMessage",
+ match_log_message, NULL, &path);
if (r < 0)
- return log_error_errno(r, "Failed to install match: %m");
+ return log_error_errno(r, "Failed to request match: %m");
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
@@ -3144,7 +3143,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
}
int main(int argc, char*argv[]) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -3167,6 +3166,9 @@ int main(int argc, char*argv[]) {
r = machinectl_main(argc, argv, bus);
finish:
+ /* make sure we terminate the bus connection first, and then close the
+ * pager, see issue #3543 for the details. */
+ sd_bus_flush_close_unref(bus);
pager_close();
polkit_agent_close();
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 330d6b3d6e..c5e59c4716 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -1078,11 +1078,10 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
return -errno;
/* This might be a slow operation, run it asynchronously in a background process */
- child = fork();
- if (child < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
-
- if (child == 0) {
+ r = safe_fork("(sd-clean)", FORK_RESET_SIGNALS, &child);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
+ if (r == 0) {
_cleanup_(image_hashmap_freep) Hashmap *images = NULL;
bool success = true;
Image *image;
diff --git a/src/machine/machined.c b/src/machine/machined.c
index d481020893..34b2024043 100644
--- a/src/machine/machined.c
+++ b/src/machine/machined.c
@@ -37,6 +37,7 @@
#include "machined.h"
#include "process-util.h"
#include "signal-util.h"
+#include "special.h"
Manager *manager_new(void) {
Manager *m;
@@ -112,7 +113,7 @@ static int manager_add_host_machine(Manager *m) {
if (!rd)
return log_oom();
- unit = strdup("-.slice");
+ unit = strdup(SPECIAL_ROOT_SLICE);
if (!unit)
return log_oom();
@@ -185,7 +186,6 @@ int manager_enumerate_machines(Manager *m) {
}
static int manager_connect_bus(Manager *m) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(m);
@@ -215,70 +215,65 @@ static int manager_connect_bus(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to add image enumerator: %m");
- r = sd_bus_add_match(m->bus,
- NULL,
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='JobRemoved',"
- "path='/org/freedesktop/systemd1'",
- match_job_removed,
- m);
+ r = sd_bus_match_signal_async(
+ m->bus,
+ NULL,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "JobRemoved",
+ match_job_removed, NULL, m);
if (r < 0)
return log_error_errno(r, "Failed to add match for JobRemoved: %m");
- r = sd_bus_add_match(m->bus,
- NULL,
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='UnitRemoved',"
- "path='/org/freedesktop/systemd1'",
- match_unit_removed,
- m);
+ r = sd_bus_match_signal_async(
+ m->bus,
+ NULL,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "UnitRemoved",
+ match_unit_removed, NULL, m);
if (r < 0)
- return log_error_errno(r, "Failed to add match for UnitRemoved: %m");
-
- r = sd_bus_add_match(m->bus,
- NULL,
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.DBus.Properties',"
- "member='PropertiesChanged',"
- "arg0='org.freedesktop.systemd1.Unit'",
- match_properties_changed,
- m);
+ return log_error_errno(r, "Failed to request match for UnitRemoved: %m");
+
+ r = sd_bus_match_signal_async(
+ m->bus,
+ NULL,
+ "org.freedesktop.systemd1",
+ NULL,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ match_properties_changed, NULL, m);
if (r < 0)
- return log_error_errno(r, "Failed to add match for PropertiesChanged: %m");
-
- r = sd_bus_add_match(m->bus,
- NULL,
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='Reloading',"
- "path='/org/freedesktop/systemd1'",
- match_reloading,
- m);
+ return log_error_errno(r, "Failed to request match for PropertiesChanged: %m");
+
+ r = sd_bus_match_signal_async(
+ m->bus,
+ NULL,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "Reloading",
+ match_reloading, NULL, m);
if (r < 0)
- return log_error_errno(r, "Failed to add match for Reloading: %m");
+ return log_error_errno(r, "Failed to request match for Reloading: %m");
- r = sd_bus_call_method(
+ r = sd_bus_call_method_async(
m->bus,
+ NULL,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"Subscribe",
- &error,
- NULL, NULL);
- if (r < 0) {
- log_error("Failed to enable subscription: %s", bus_error_message(&error, r));
- return r;
- }
+ NULL, NULL,
+ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable subscription: %m");
- r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0);
+ r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.machine1", 0, NULL, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to register name: %m");
+ return log_error_errno(r, "Failed to request name: %m");
r = sd_bus_attach_event(m->bus, m->event, 0);
if (r < 0)
diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c
index da3647e7e2..0cd9f07094 100644
--- a/src/mount/mount-tool.c
+++ b/src/mount/mount-tool.c
@@ -40,7 +40,9 @@
#include "stat-util.h"
#include "strv.h"
#include "udev-util.h"
+#include "unit-def.h"
#include "unit-name.h"
+#include "user-util.h"
#include "terminal-util.h"
enum {
@@ -69,6 +71,8 @@ static usec_t arg_timeout_idle = USEC_INFINITY;
static bool arg_timeout_idle_set = false;
static char **arg_automount_property = NULL;
static int arg_bind_device = -1;
+static uid_t arg_uid = UID_INVALID;
+static gid_t arg_gid = GID_INVALID;
static bool arg_fsck = true;
static bool arg_aggressive_gc = false;
@@ -89,6 +93,7 @@ static void help(void) {
" --discover Discover mount device metadata\n"
" -t --type=TYPE File system type\n"
" -o --options=OPTIONS Mount options\n"
+ " --owner=USER Add uid= and gid= options for USER\n"
" --fsck=no Don't run file system check before mount\n"
" --description=TEXT Description for unit\n"
" -p --property=NAME=VALUE Set mount unit property\n"
@@ -116,6 +121,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_DISCOVER,
ARG_MOUNT_TYPE,
ARG_MOUNT_OPTIONS,
+ ARG_OWNER,
ARG_FSCK,
ARG_DESCRIPTION,
ARG_TIMEOUT_IDLE,
@@ -139,6 +145,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "discover", no_argument, NULL, ARG_DISCOVER },
{ "type", required_argument, NULL, 't' },
{ "options", required_argument, NULL, 'o' },
+ { "owner", required_argument, NULL, ARG_OWNER },
{ "fsck", required_argument, NULL, ARG_FSCK },
{ "description", required_argument, NULL, ARG_DESCRIPTION },
{ "property", required_argument, NULL, 'p' },
@@ -220,6 +227,18 @@ static int parse_argv(int argc, char *argv[]) {
return log_oom();
break;
+ case ARG_OWNER: {
+ const char *user = optarg;
+
+ r = get_user_creds(&user, &arg_uid, &arg_gid, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r,
+ r == -EBADMSG ? "UID or GID of user %s are invalid."
+ : "Cannot use \"%s\" as owner: %m",
+ optarg);
+ break;
+ }
+
case ARG_FSCK:
r = parse_boolean(optarg);
if (r < 0)
@@ -385,7 +404,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
+static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) {
int r;
if (!isempty(arg_description)) {
@@ -414,7 +433,7 @@ static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
return r;
}
- r = bus_append_unit_property_assignment_many(m, properties);
+ r = bus_append_unit_property_assignment_many(m, t, properties);
if (r < 0)
return r;
@@ -422,11 +441,12 @@ static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
}
static int transient_mount_set_properties(sd_bus_message *m) {
+ _cleanup_free_ char *options = NULL;
int r;
assert(m);
- r = transient_unit_set_properties(m, arg_property);
+ r = transient_unit_set_properties(m, UNIT_MOUNT, arg_property);
if (r < 0)
return r;
@@ -442,12 +462,25 @@ static int transient_mount_set_properties(sd_bus_message *m) {
return r;
}
- if (arg_mount_options) {
- r = sd_bus_message_append(m, "(sv)", "Options", "s", arg_mount_options);
+ /* Prepend uid=…,gid=… if arg_uid is set */
+ if (arg_uid != UID_INVALID) {
+ r = asprintf(&options,
+ "uid=" UID_FMT ",gid=" GID_FMT "%s%s",
+ arg_uid, arg_gid,
+ arg_mount_options ? "," : "", arg_mount_options);
if (r < 0)
- return r;
+ return -ENOMEM;
}
+ if (options || arg_mount_options) {
+ log_debug("Using mount options: %s", options ?: arg_mount_options);
+
+ r = sd_bus_message_append(m, "(sv)", "Options", "s", options ?: arg_mount_options);
+ if (r < 0)
+ return r;
+ } else
+ log_debug("Not using any mount options");
+
if (arg_fsck) {
_cleanup_free_ char *fsck = NULL;
@@ -471,7 +504,7 @@ static int transient_automount_set_properties(sd_bus_message *m) {
assert(m);
- r = transient_unit_set_properties(m, arg_automount_property);
+ r = transient_unit_set_properties(m, UNIT_AUTOMOUNT, arg_automount_property);
if (r < 0)
return r;
@@ -1530,7 +1563,7 @@ finish:
}
int main(int argc, char* argv[]) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ sd_bus *bus = NULL;
int r;
log_parse_environment();
@@ -1604,6 +1637,23 @@ int main(int argc, char* argv[]) {
}
}
+ /* The kernel (properly) refuses mounting file systems with unknown uid=,gid= options,
+ * but not for all filesystem types. Let's try to catch the cases where the option
+ * would be used if the file system does not support it. It is also possible to
+ * autodetect the file system, but that's only possible with disk-based file systems
+ * which incidentally seem to be implemented more carefully and reject unknown options,
+ * so it's probably OK that we do the check only when the type is specified.
+ */
+ if (arg_mount_type &&
+ !streq(arg_mount_type, "auto") &&
+ arg_uid != UID_INVALID &&
+ !fstype_can_uid_gid(arg_mount_type)) {
+ log_error("File system type %s is not known to support uid=/gid=, refusing.",
+ arg_mount_type);
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
switch (arg_action) {
case ACTION_MOUNT:
@@ -1620,6 +1670,9 @@ int main(int argc, char* argv[]) {
}
finish:
+ /* make sure we terminate the bus connection first, and then close the
+ * pager, see issue #3543 for the details. */
+ bus = sd_bus_flush_close_unref(bus);
pager_close();
free(arg_mount_what);
diff --git a/src/network/meson.build b/src/network/meson.build
index f97484eb26..e1ac0f13dc 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -46,6 +46,8 @@ sources = files('''
netdev/geneve.h
netdev/vxcan.c
netdev/vxcan.h
+ netdev/wireguard.c
+ netdev/wireguard.h
networkd-address-label.c
networkd-address-label.h
networkd-address-pool.c
@@ -151,7 +153,7 @@ if conf.get('ENABLE_NETWORKD') == 1
[['src/network/test-network.c'],
[libnetworkd_core,
- libudev_internal,
+ libudev_static,
libsystemd_network,
libshared],
[threads]],
@@ -166,7 +168,7 @@ if conf.get('ENABLE_NETWORKD') == 1
'src/network/test-network-tables.c',
test_tables_h],
[libnetworkd_core,
- libudev_internal,
+ libudev_static,
libudev_core,
libsystemd_network,
libshared],
diff --git a/src/network/netdev/ipvlan.c b/src/network/netdev/ipvlan.c
index df9487418b..856f5bd805 100644
--- a/src/network/netdev/ipvlan.c
+++ b/src/network/netdev/ipvlan.c
@@ -27,11 +27,21 @@
static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = {
[NETDEV_IPVLAN_MODE_L2] = "L2",
[NETDEV_IPVLAN_MODE_L3] = "L3",
+ [NETDEV_IPVLAN_MODE_L3S] = "L3S",
};
DEFINE_STRING_TABLE_LOOKUP(ipvlan_mode, IPVlanMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipvlan_mode, ipvlan_mode, IPVlanMode, "Failed to parse ipvlan mode");
+static const char* const ipvlan_flags_table[_NETDEV_IPVLAN_FLAGS_MAX] = {
+ [NETDEV_IPVLAN_FLAGS_BRIGDE] = "bridge",
+ [NETDEV_IPVLAN_FLAGS_PRIVATE] = "private",
+ [NETDEV_IPVLAN_FLAGS_VEPA] = "vepa",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ipvlan_flags, IPVlanFlags);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_ipvlan_flags, ipvlan_flags, IPVlanFlags, "Failed to parse ipvlan flags");
+
static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) {
IPVlan *m;
int r;
@@ -50,6 +60,12 @@ static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netl
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPVLAN_MODE attribute: %m");
}
+ if (m->flags != _NETDEV_IPVLAN_FLAGS_INVALID) {
+ r = sd_netlink_message_append_u16(req, IFLA_IPVLAN_FLAGS, m->flags);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPVLAN_FLAGS attribute: %m");
+ }
+
return 0;
}
@@ -63,6 +79,7 @@ static void ipvlan_init(NetDev *n) {
assert(m);
m->mode = _NETDEV_IPVLAN_MODE_INVALID;
+ m->flags = _NETDEV_IPVLAN_FLAGS_INVALID;
}
const NetDevVTable ipvlan_vtable = {
diff --git a/src/network/netdev/ipvlan.h b/src/network/netdev/ipvlan.h
index cb43db4348..b6bc1ac739 100644
--- a/src/network/netdev/ipvlan.h
+++ b/src/network/netdev/ipvlan.h
@@ -20,20 +20,33 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <linux/if_link.h>
+
#include "missing.h"
#include "netdev/netdev.h"
+
typedef enum IPVlanMode {
NETDEV_IPVLAN_MODE_L2 = IPVLAN_MODE_L2,
NETDEV_IPVLAN_MODE_L3 = IPVLAN_MODE_L3,
+ NETDEV_IPVLAN_MODE_L3S = IPVLAN_MODE_L3S,
_NETDEV_IPVLAN_MODE_MAX,
_NETDEV_IPVLAN_MODE_INVALID = -1
} IPVlanMode;
+typedef enum IPVlanFlags {
+ NETDEV_IPVLAN_FLAGS_BRIGDE,
+ NETDEV_IPVLAN_FLAGS_PRIVATE = IPVLAN_F_PRIVATE,
+ NETDEV_IPVLAN_FLAGS_VEPA = IPVLAN_F_VEPA,
+ _NETDEV_IPVLAN_FLAGS_MAX,
+ _NETDEV_IPVLAN_FLAGS_INVALID = -1
+} IPVlanFlags;
+
typedef struct IPVlan {
NetDev meta;
IPVlanMode mode;
+ IPVlanFlags flags;
} IPVlan;
DEFINE_NETDEV_CAST(IPVLAN, IPVlan);
@@ -42,4 +55,8 @@ extern const NetDevVTable ipvlan_vtable;
const char *ipvlan_mode_to_string(IPVlanMode d) _const_;
IPVlanMode ipvlan_mode_from_string(const char *d) _pure_;
+const char *ipvlan_flags_to_string(IPVlanFlags d) _const_;
+IPVlanFlags ipvlan_flags_from_string(const char *d) _pure_;
+
int config_parse_ipvlan_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_ipvlan_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf
index 628e6648b7..ba6268fa66 100644
--- a/src/network/netdev/netdev-gperf.gperf
+++ b/src/network/netdev/netdev-gperf.gperf
@@ -18,6 +18,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "netdev/vrf.h"
#include "netdev/netdev.h"
#include "netdev/vxcan.h"
+#include "netdev/wireguard.h"
#include "vlan-util.h"
%}
struct ConfigPerfItem;
@@ -31,114 +32,125 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host)
-Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel)
-Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch)
-NetDev.Description, config_parse_string, 0, offsetof(NetDev, description)
-NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname)
-NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind)
-NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu)
-NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac)
-VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id)
-VLAN.GVRP, config_parse_tristate, 0, offsetof(VLan, gvrp)
-VLAN.MVRP, config_parse_tristate, 0, offsetof(VLan, mvrp)
-VLAN.LooseBinding, config_parse_tristate, 0, offsetof(VLan, loose_binding)
-VLAN.ReorderHeader, config_parse_tristate, 0, offsetof(VLan, reorder_hdr)
-MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
-MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
-IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
-Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local)
-Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote)
-Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos)
-Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl)
-Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key)
-Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey)
-Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey)
-Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc)
-Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode)
-Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel)
-Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp)
-Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit)
-Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent)
-Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
-Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
-VXCAN.Peer, config_parse_ifname, 0, offsetof(VxCan, ifname_peer)
-VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id)
-VXLAN.Group, config_parse_vxlan_address, 0, offsetof(VxLan, remote)
-VXLAN.Local, config_parse_vxlan_address, 0, offsetof(VxLan, local)
-VXLAN.Remote, config_parse_vxlan_address, 0, offsetof(VxLan, remote)
-VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos)
-VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl)
-VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning)
-VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
-VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
-VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss)
-VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss)
-VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit)
-VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
-VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
-VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
-VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
-VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
-VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
-VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx)
-VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx)
-VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing)
-VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy)
-VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb)
-VXLAN.PortRange, config_parse_port_range, 0, 0
-VXLAN.DestinationPort, config_parse_ip_port, 0, offsetof(VxLan, dest_port)
-VXLAN.FlowLabel, config_parse_flow_label, 0, 0
-GENEVE.Id, config_parse_geneve_vni, 0, offsetof(Geneve, id)
-GENEVE.Remote, config_parse_geneve_address, 0, offsetof(Geneve, remote)
-GENEVE.TOS, config_parse_uint8, 0, offsetof(Geneve, tos)
-GENEVE.TTL, config_parse_uint8, 0, offsetof(Geneve, ttl)
-GENEVE.UDPChecksum, config_parse_bool, 0, offsetof(Geneve, udpcsum)
-GENEVE.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx)
-GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx)
-GENEVE.DestinationPort, config_parse_ip_port, 0, offsetof(Geneve, dest_port)
-GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0
-Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
-Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
-Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
-Tun.User, config_parse_string, 0, offsetof(TunTap, user_name)
-Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name)
-Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
-Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
-Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
-Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
-Tap.User, config_parse_string, 0, offsetof(TunTap, user_name)
-Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name)
-Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode)
-Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy)
-Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate)
-Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select)
-Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac)
-Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0
-Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate)
-Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets)
-Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect)
-Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp)
-Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave)
-Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp)
-Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active)
-Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links)
-Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon)
-Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay)
-Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay)
-Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval)
-Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval)
-Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time)
-Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age)
-Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time)
-Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay)
-Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority)
-Bridge.GroupForwardMask, config_parse_uint16, 0, offsetof(Bridge, group_fwd_mask)
-Bridge.DefaultPVID, config_parse_default_port_vlanid, 0, offsetof(Bridge, default_pvid)
-Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier)
-Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping)
-Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering)
-Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp)
-VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table) /* deprecated */
-VRF.Table, config_parse_route_table, 0, offsetof(Vrf, table)
+Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host)
+Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel_cmdline)
+Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(NetDev, match_kernel_version)
+Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch)
+NetDev.Description, config_parse_string, 0, offsetof(NetDev, description)
+NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname)
+NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind)
+NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu)
+NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac)
+VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id)
+VLAN.GVRP, config_parse_tristate, 0, offsetof(VLan, gvrp)
+VLAN.MVRP, config_parse_tristate, 0, offsetof(VLan, mvrp)
+VLAN.LooseBinding, config_parse_tristate, 0, offsetof(VLan, loose_binding)
+VLAN.ReorderHeader, config_parse_tristate, 0, offsetof(VLan, reorder_hdr)
+MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
+MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
+IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
+IPVLAN.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags)
+Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local)
+Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote)
+Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos)
+Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl)
+Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key)
+Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey)
+Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey)
+Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc)
+Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode)
+Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel)
+Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp)
+Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit)
+Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent)
+Tunnel.AllowLocalRemote, config_parse_tristate, 0, offsetof(Tunnel, allow_localremote)
+Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
+Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
+VXCAN.Peer, config_parse_ifname, 0, offsetof(VxCan, ifname_peer)
+VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id)
+VXLAN.Group, config_parse_vxlan_address, 0, offsetof(VxLan, remote)
+VXLAN.Local, config_parse_vxlan_address, 0, offsetof(VxLan, local)
+VXLAN.Remote, config_parse_vxlan_address, 0, offsetof(VxLan, remote)
+VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos)
+VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl)
+VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning)
+VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
+VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
+VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss)
+VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss)
+VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit)
+VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
+VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
+VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
+VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
+VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
+VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
+VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx)
+VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx)
+VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing)
+VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy)
+VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb)
+VXLAN.PortRange, config_parse_port_range, 0, 0
+VXLAN.DestinationPort, config_parse_ip_port, 0, offsetof(VxLan, dest_port)
+VXLAN.FlowLabel, config_parse_flow_label, 0, 0
+GENEVE.Id, config_parse_geneve_vni, 0, offsetof(Geneve, id)
+GENEVE.Remote, config_parse_geneve_address, 0, offsetof(Geneve, remote)
+GENEVE.TOS, config_parse_uint8, 0, offsetof(Geneve, tos)
+GENEVE.TTL, config_parse_uint8, 0, offsetof(Geneve, ttl)
+GENEVE.UDPChecksum, config_parse_bool, 0, offsetof(Geneve, udpcsum)
+GENEVE.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx)
+GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx)
+GENEVE.DestinationPort, config_parse_ip_port, 0, offsetof(Geneve, dest_port)
+GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0
+Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
+Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
+Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
+Tun.User, config_parse_string, 0, offsetof(TunTap, user_name)
+Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name)
+Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
+Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
+Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
+Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
+Tap.User, config_parse_string, 0, offsetof(TunTap, user_name)
+Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name)
+Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode)
+Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy)
+Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate)
+Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select)
+Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac)
+Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0
+Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate)
+Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets)
+Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect)
+Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp)
+Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave)
+Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp)
+Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active)
+Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links)
+Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon)
+Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay)
+Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay)
+Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval)
+Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval)
+Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time)
+Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age)
+Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time)
+Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay)
+Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority)
+Bridge.GroupForwardMask, config_parse_uint16, 0, offsetof(Bridge, group_fwd_mask)
+Bridge.DefaultPVID, config_parse_default_port_vlanid, 0, offsetof(Bridge, default_pvid)
+Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier)
+Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping)
+Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering)
+Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp)
+VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table) /* deprecated */
+VRF.Table, config_parse_route_table, 0, offsetof(Vrf, table)
+WireGuard.FwMark, config_parse_unsigned, 0, offsetof(Wireguard, fwmark)
+WireGuard.ListenPort, config_parse_wireguard_listen_port, 0, offsetof(Wireguard, port)
+WireGuard.PrivateKey, config_parse_wireguard_private_key, 0, 0
+WireGuardPeer.AllowedIPs, config_parse_wireguard_allowed_ips, 0, 0
+WireGuardPeer.Endpoint, config_parse_wireguard_endpoint, 0, 0
+WireGuardPeer.PublicKey, config_parse_wireguard_public_key, 0, 0
+WireGuardPeer.PresharedKey, config_parse_wireguard_preshared_key, 0, 0
+WireGuardPeer.PersistentKeepalive, config_parse_wireguard_keepalive, 0, 0
diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c
index 5530760e19..93648e1be0 100644
--- a/src/network/netdev/netdev.c
+++ b/src/network/netdev/netdev.c
@@ -49,6 +49,7 @@
#include "netdev/vrf.h"
#include "netdev/vcan.h"
#include "netdev/vxcan.h"
+#include "netdev/wireguard.h"
const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_BRIDGE] = &bridge_vtable,
@@ -75,6 +76,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_VCAN] = &vcan_vtable,
[NETDEV_KIND_GENEVE] = &geneve_vtable,
[NETDEV_KIND_VXCAN] = &vxcan_vtable,
+ [NETDEV_KIND_WIREGUARD] = &wireguard_vtable,
};
static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
@@ -102,6 +104,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_VCAN] = "vcan",
[NETDEV_KIND_GENEVE] = "geneve",
[NETDEV_KIND_VXCAN] = "vxcan",
+ [NETDEV_KIND_WIREGUARD] = "wireguard",
};
DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
@@ -111,10 +114,10 @@ static void netdev_cancel_callbacks(NetDev *netdev) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
netdev_join_callback *callback;
- if (!netdev)
+ if (!netdev || !netdev->manager)
return;
- rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
+ rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m);
while ((callback = netdev->callbacks)) {
if (m) {
@@ -138,7 +141,7 @@ static void netdev_free(NetDev *netdev) {
netdev_cancel_callbacks(netdev);
- if (netdev->ifname)
+ if (netdev->ifname && netdev->manager)
hashmap_remove(netdev->manager->netdevs, netdev->ifname);
free(netdev->filename);
@@ -149,10 +152,19 @@ static void netdev_free(NetDev *netdev) {
condition_free_list(netdev->match_host);
condition_free_list(netdev->match_virt);
- condition_free_list(netdev->match_kernel);
+ condition_free_list(netdev->match_kernel_cmdline);
+ condition_free_list(netdev->match_kernel_version);
condition_free_list(netdev->match_arch);
- if (NETDEV_VTABLE(netdev) &&
+ /* Invoke the per-kind done() destructor, but only if the state field is initialized. We conditionalize that
+ * because we parse .netdev files twice: once to determine the kind (with a short, minimal NetDev structure
+ * allocation, with no room for per-kind fields), and once to read the kind's properties (with a full,
+ * comprehensive NetDev structure allocation with enough space for whatever the specific kind needs). Now, in
+ * the first case we shouldn't try to destruct the per-kind NetDev fields on destruction, in the second case we
+ * should. We use the state field to discern the two cases: it's _NETDEV_STATE_INVALID on the first "raw"
+ * call. */
+ if (netdev->state != _NETDEV_STATE_INVALID &&
+ NETDEV_VTABLE(netdev) &&
NETDEV_VTABLE(netdev)->done)
NETDEV_VTABLE(netdev)->done(netdev);
@@ -321,7 +333,7 @@ int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t call
} else if (IN_SET(netdev->state, NETDEV_STATE_LINGER, NETDEV_STATE_FAILED)) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- r = rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
+ r = rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m);
if (r >= 0)
callback(netdev->manager->rtnl, m, link);
} else {
@@ -601,8 +613,7 @@ int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callbac
}
static int netdev_load_one(Manager *manager, const char *filename) {
- _cleanup_netdev_unref_ NetDev *netdev = NULL;
- _cleanup_free_ NetDev *netdev_raw = NULL;
+ _cleanup_netdev_unref_ NetDev *netdev_raw = NULL, *netdev = NULL;
_cleanup_fclose_ FILE *file = NULL;
const char *dropin_dirname;
bool independent = false;
@@ -615,8 +626,8 @@ static int netdev_load_one(Manager *manager, const char *filename) {
if (!file) {
if (errno == ENOENT)
return 0;
- else
- return -errno;
+
+ return -errno;
}
if (null_or_empty_fd(fileno(file))) {
@@ -628,24 +639,23 @@ static int netdev_load_one(Manager *manager, const char *filename) {
if (!netdev_raw)
return log_oom();
+ netdev_raw->n_ref = 1;
netdev_raw->kind = _NETDEV_KIND_INVALID;
- dropin_dirname = strjoina(basename(filename), ".d");
+ netdev_raw->state = _NETDEV_STATE_INVALID; /* an invalid state means done() of the implementation won't be called on destruction */
+ dropin_dirname = strjoina(basename(filename), ".d");
r = config_parse_many(filename, network_dirs, dropin_dirname,
"Match\0NetDev\0",
config_item_perf_lookup, network_netdev_gperf_lookup,
- CONFIG_PARSE_WARN, netdev_raw);
+ CONFIG_PARSE_WARN|CONFIG_PARSE_RELAXED, netdev_raw);
if (r < 0)
return r;
- r = fseek(file, 0, SEEK_SET);
- if (r < 0)
- return -errno;
-
/* skip out early if configuration does not match the environment */
if (net_match_config(NULL, NULL, NULL, NULL, NULL,
netdev_raw->match_host, netdev_raw->match_virt,
- netdev_raw->match_kernel, netdev_raw->match_arch,
+ netdev_raw->match_kernel_cmdline, netdev_raw->match_kernel_version,
+ netdev_raw->match_arch,
NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
return 0;
@@ -659,15 +669,18 @@ static int netdev_load_one(Manager *manager, const char *filename) {
return 0;
}
+ r = fseek(file, 0, SEEK_SET);
+ if (r < 0)
+ return -errno;
+
netdev = malloc0(NETDEV_VTABLE(netdev_raw)->object_size);
if (!netdev)
return log_oom();
netdev->n_ref = 1;
netdev->manager = manager;
- netdev->state = _NETDEV_STATE_INVALID;
netdev->kind = netdev_raw->kind;
- netdev->ifname = netdev_raw->ifname;
+ netdev->state = NETDEV_STATE_LOADING; /* we initialize the state here for the first time, so that done() will be called on destruction */
if (NETDEV_VTABLE(netdev)->init)
NETDEV_VTABLE(netdev)->init(netdev);
diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h
index ec65251464..51b3ea7a8f 100644
--- a/src/network/netdev/netdev.h
+++ b/src/network/netdev/netdev.h
@@ -60,11 +60,13 @@ typedef enum NetDevKind {
NETDEV_KIND_VCAN,
NETDEV_KIND_GENEVE,
NETDEV_KIND_VXCAN,
+ NETDEV_KIND_WIREGUARD,
_NETDEV_KIND_MAX,
_NETDEV_KIND_INVALID = -1
} NetDevKind;
typedef enum NetDevState {
+ NETDEV_STATE_LOADING,
NETDEV_STATE_FAILED,
NETDEV_STATE_CREATING,
NETDEV_STATE_READY,
@@ -93,7 +95,8 @@ typedef struct NetDev {
Condition *match_host;
Condition *match_virt;
- Condition *match_kernel;
+ Condition *match_kernel_cmdline;
+ Condition *match_kernel_version;
Condition *match_arch;
NetDevState state;
@@ -143,12 +146,14 @@ typedef struct NetDevVTable {
extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX];
-#define NETDEV_VTABLE(n) netdev_vtable[(n)->kind]
+#define NETDEV_VTABLE(n) ((n)->kind != _NETDEV_KIND_INVALID ? netdev_vtable[(n)->kind] : NULL)
/* For casting a netdev into the various netdev kinds */
#define DEFINE_NETDEV_CAST(UPPERCASE, MixedCase) \
static inline MixedCase* UPPERCASE(NetDev *n) { \
- if (_unlikely_(!n || n->kind != NETDEV_KIND_##UPPERCASE)) \
+ if (_unlikely_(!n || \
+ n->kind != NETDEV_KIND_##UPPERCASE) || \
+ n->state == _NETDEV_STATE_INVALID) \
return NULL; \
\
return (MixedCase*) n; \
diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c
index 8d6d54d567..a6a3c701f9 100644
--- a/src/network/netdev/tunnel.c
+++ b/src/network/netdev/tunnel.c
@@ -37,6 +37,7 @@
#define DEFAULT_TNL_HOP_LIMIT 64
#define IP6_FLOWINFO_FLOWLABEL htobe32(0x000FFFFF)
+#define IP6_TNL_F_ALLOW_LOCAL_REMOTE 0x40
static const char* const ip6tnl_mode_table[_NETDEV_IP6_TNL_MODE_MAX] = {
[NETDEV_IP6_TNL_MODE_IP6IP6] = "ip6ip6",
@@ -60,7 +61,6 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlin
r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
-
}
r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in);
@@ -95,7 +95,6 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink
r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
-
}
r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in);
@@ -336,6 +335,9 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl
if (t->copy_dscp)
t->flags |= IP6_TNL_F_RCV_DSCP_COPY;
+ if (t->allow_localremote != -1)
+ SET_FLAG(t->flags, IP6_TNL_F_ALLOW_LOCAL_REMOTE, t->allow_localremote);
+
if (t->encap_limit != IPV6_DEFAULT_TNL_ENCAP_LIMIT) {
r = sd_netlink_message_append_u8(m, IFLA_IPTUN_ENCAP_LIMIT, t->encap_limit);
if (r < 0)
@@ -682,6 +684,7 @@ static void ip6tnl_init(NetDev *n) {
t->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
t->ip6tnl_mode = _NETDEV_IP6_TNL_MODE_INVALID;
t->ipv6_flowlabel = _NETDEV_IPV6_FLOWLABEL_INVALID;
+ t->allow_localremote = -1;
}
const NetDevVTable ipip_vtable = {
diff --git a/src/network/netdev/tunnel.h b/src/network/netdev/tunnel.h
index 67f8fe35c7..7ffafe9e98 100644
--- a/src/network/netdev/tunnel.h
+++ b/src/network/netdev/tunnel.h
@@ -45,6 +45,7 @@ typedef struct Tunnel {
int family;
int ipv6_flowlabel;
+ int allow_localremote;
unsigned ttl;
unsigned tos;
diff --git a/src/network/netdev/tuntap.c b/src/network/netdev/tuntap.c
index 4597a7feeb..4fc9b610af 100644
--- a/src/network/netdev/tuntap.c
+++ b/src/network/netdev/tuntap.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <fcntl.h>
#include <linux/if_tun.h>
#include <net/if.h>
diff --git a/src/network/netdev/veth.c b/src/network/netdev/veth.c
index 9220b3200f..2a2f50e345 100644
--- a/src/network/netdev/veth.c
+++ b/src/network/netdev/veth.c
@@ -18,8 +18,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <net/if.h>
+#include <errno.h>
#include <linux/veth.h>
+#include <net/if.h>
#include "sd-netlink.h"
diff --git a/src/network/netdev/vlan.c b/src/network/netdev/vlan.c
index 3a0100d7e6..e7c0e7602a 100644
--- a/src/network/netdev/vlan.c
+++ b/src/network/netdev/vlan.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <linux/if_vlan.h>
#include <net/if.h>
diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c
new file mode 100644
index 0000000000..f1f4bab475
--- /dev/null
+++ b/src/network/netdev/wireguard.c
@@ -0,0 +1,721 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016-2017 Jörg Thalheim <joerg@thalheim.io>
+ Copyright 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+#include "alloc-util.h"
+#include "parse-util.h"
+#include "fd-util.h"
+#include "strv.h"
+#include "hexdecoct.h"
+#include "string-util.h"
+#include "wireguard.h"
+#include "networkd-link.h"
+#include "networkd-util.h"
+#include "networkd-manager.h"
+#include "wireguard-netlink.h"
+
+static void resolve_endpoints(NetDev *netdev);
+
+static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) {
+ WireguardPeer *peer;
+
+ assert(w);
+
+ if (w->last_peer_section == section && w->peers)
+ return w->peers;
+
+ peer = new0(WireguardPeer, 1);
+ if (!peer)
+ return NULL;
+ peer->flags = WGPEER_F_REPLACE_ALLOWEDIPS;
+
+ LIST_PREPEND(peers, w->peers, peer);
+ w->last_peer_section = section;
+
+ return peer;
+}
+
+static int set_wireguard_interface(NetDev *netdev) {
+ int r;
+ unsigned int i, j;
+ WireguardPeer *peer, *peer_start;
+ WireguardIPmask *mask, *mask_start = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
+ Wireguard *w;
+ uint32_t serial;
+
+ assert(netdev);
+ w = WIREGUARD(netdev);
+ assert(w);
+
+ peer_start = w->peers;
+
+ do {
+ message = sd_netlink_message_unref(message);
+
+ r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
+
+ r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
+
+ if (peer_start == w->peers) {
+ r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
+
+ r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
+
+ r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
+
+ r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
+ }
+
+ r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
+
+ i = 0;
+
+ LIST_FOREACH(peers, peer, peer_start) {
+ r = sd_netlink_message_open_array(message, ++i);
+ if (r < 0)
+ break;
+
+ r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key));
+ if (r < 0)
+ break;
+
+ if (!mask_start) {
+ r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN);
+ if (r < 0)
+ break;
+
+ r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags);
+ if (r < 0)
+ break;
+
+ r = sd_netlink_message_append_u32(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
+ if (r < 0)
+ break;
+
+ if (peer->endpoint.sa.sa_family == AF_INET) {
+ r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in, sizeof(peer->endpoint.in));
+ if (r < 0)
+ break;
+ } else if (peer->endpoint.sa.sa_family == AF_INET6) {
+ r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in6, sizeof(peer->endpoint.in6));
+ if (r < 0)
+ break;
+ }
+
+ mask_start = peer->ipmasks;
+ }
+
+ r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS);
+ if (r < 0) {
+ mask_start = NULL;
+ break;
+ }
+ j = 0;
+ LIST_FOREACH(ipmasks, mask, mask_start) {
+ r = sd_netlink_message_open_array(message, ++j);
+ if (r < 0)
+ break;
+
+ r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family);
+ if (r < 0)
+ break;
+
+ if (mask->family == AF_INET) {
+ r = sd_netlink_message_append_in_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in);
+ if (r < 0)
+ break;
+ } else if (mask->family == AF_INET6) {
+ r = sd_netlink_message_append_in6_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in6);
+ if (r < 0)
+ break;
+ }
+
+ r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr);
+ if (r < 0)
+ break;
+
+ r = sd_netlink_message_close_container(message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
+ }
+ mask_start = mask;
+ if (mask_start) {
+ r = sd_netlink_message_cancel_array(message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m");
+ }
+ r = sd_netlink_message_close_container(message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
+
+ r = sd_netlink_message_close_container(message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m");
+ }
+
+ peer_start = peer;
+ if (peer_start && !mask_start) {
+ r = sd_netlink_message_cancel_array(message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m");
+ }
+
+ r = sd_netlink_message_close_container(message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
+
+ r = sd_netlink_send(netdev->manager->genl, message, &serial);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
+
+ } while (peer || mask_start);
+
+ return 0;
+}
+
+static WireguardEndpoint* wireguard_endpoint_free(WireguardEndpoint *e) {
+ if (!e)
+ return NULL;
+ netdev_unref(e->netdev);
+ e->host = mfree(e->host);
+ e->port = mfree(e->port);
+ return mfree(e);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(WireguardEndpoint*, wireguard_endpoint_free);
+
+static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
+ NetDev *netdev = userdata;
+ Wireguard *w;
+
+ assert(netdev);
+ w = WIREGUARD(netdev);
+ assert(w);
+
+ w->resolve_retry_event_source = sd_event_source_unref(w->resolve_retry_event_source);
+
+ w->unresolved_endpoints = w->failed_endpoints;
+ w->failed_endpoints = NULL;
+
+ resolve_endpoints(netdev);
+
+ return 0;
+}
+
+/*
+ * Given the number of retries this function will return will an exponential
+ * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds.
+ */
+static int exponential_backoff_milliseconds(unsigned n_retries) {
+ return (2 << MAX(n_retries, 7U)) * 100 * USEC_PER_MSEC;
+}
+
+static int wireguard_resolve_handler(sd_resolve_query *q,
+ int ret,
+ const struct addrinfo *ai,
+ void *userdata) {
+ NetDev *netdev;
+ Wireguard *w;
+ _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *e;
+ int r;
+
+ assert(userdata);
+ e = userdata;
+ netdev = e->netdev;
+
+ assert(netdev);
+ w = WIREGUARD(netdev);
+ assert(w);
+
+ w->resolve_query = sd_resolve_query_unref(w->resolve_query);
+
+ if (ret != 0) {
+ log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", e->host, e->port, gai_strerror(ret));
+ LIST_PREPEND(endpoints, w->failed_endpoints, e);
+ e = NULL;
+ } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) ||
+ (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
+ memcpy(&e->peer->endpoint, ai->ai_addr, ai->ai_addrlen);
+ else
+ log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint: %s:%s", e->host, e->port);
+
+ if (w->unresolved_endpoints) {
+ resolve_endpoints(netdev);
+ return 0;
+ }
+
+ set_wireguard_interface(netdev);
+ if (w->failed_endpoints) {
+ w->n_retries++;
+ r = sd_event_add_time(netdev->manager->event,
+ &w->resolve_retry_event_source,
+ CLOCK_MONOTONIC,
+ now(CLOCK_MONOTONIC) + exponential_backoff_milliseconds(w->n_retries),
+ 0,
+ on_resolve_retry,
+ netdev);
+ if (r < 0)
+ log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m");
+ }
+
+ return 0;
+}
+
+static void resolve_endpoints(NetDev *netdev) {
+ int r = 0;
+ Wireguard *w;
+ WireguardEndpoint *endpoint;
+ static const struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP
+ };
+
+ assert(netdev);
+ w = WIREGUARD(netdev);
+ assert(w);
+
+ LIST_FOREACH(endpoints, endpoint, w->unresolved_endpoints) {
+ r = sd_resolve_getaddrinfo(netdev->manager->resolve,
+ &w->resolve_query,
+ endpoint->host,
+ endpoint->port,
+ &hints,
+ wireguard_resolve_handler,
+ endpoint);
+
+ if (r == -ENOBUFS)
+ break;
+
+ LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint);
+
+ if (r < 0)
+ log_netdev_error_errno(netdev, r, "Failed create resolver: %m");
+ }
+}
+
+
+static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
+ Wireguard *w;
+
+ assert(netdev);
+ w = WIREGUARD(netdev);
+ assert(w);
+
+ set_wireguard_interface(netdev);
+ resolve_endpoints(netdev);
+ return 0;
+}
+
+int config_parse_wireguard_listen_port(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ uint16_t *s = data;
+ uint16_t port = 0;
+ int r;
+
+ assert(rvalue);
+ assert(data);
+
+ if (!streq(rvalue, "auto")) {
+ r = parse_ip_port(rvalue, &port);
+ if (r < 0)
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid port specification, ignoring assignment: %s", rvalue);
+ }
+
+ *s = port;
+
+ return 0;
+}
+
+static int parse_wireguard_key(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ _cleanup_free_ void *key = NULL;
+ size_t len;
+ int r;
+
+ assert(filename);
+ assert(rvalue);
+ assert(userdata);
+
+ r = unbase64mem(rvalue, strlen(rvalue), &key, &len);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse wireguard key \"%s\", ignoring assignment: %m", rvalue);
+ return 0;
+ }
+ if (len != WG_KEY_LEN) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Wireguard key is too short, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ memcpy(userdata, key, WG_KEY_LEN);
+ return true;
+}
+
+int config_parse_wireguard_private_key(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Wireguard *w;
+
+ assert(data);
+
+ w = WIREGUARD(data);
+
+ assert(w);
+
+ return parse_wireguard_key(unit,
+ filename,
+ line,
+ section,
+ section_line,
+ lvalue,
+ ltype,
+ rvalue,
+ data,
+ &w->private_key);
+
+}
+
+int config_parse_wireguard_preshared_key(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Wireguard *w;
+ WireguardPeer *peer;
+
+ assert(data);
+
+ w = WIREGUARD(data);
+
+ assert(w);
+
+ peer = wireguard_peer_new(w, section_line);
+ if (!peer)
+ return log_oom();
+
+ return parse_wireguard_key(unit,
+ filename,
+ line,
+ section,
+ section_line,
+ lvalue,
+ ltype,
+ rvalue,
+ data,
+ peer->preshared_key);
+}
+
+
+int config_parse_wireguard_public_key(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Wireguard *w;
+ WireguardPeer *peer;
+
+ assert(data);
+
+ w = WIREGUARD(data);
+
+ assert(w);
+
+ peer = wireguard_peer_new(w, section_line);
+ if (!peer)
+ return log_oom();
+
+ return parse_wireguard_key(unit,
+ filename,
+ line,
+ section,
+ section_line,
+ lvalue,
+ ltype,
+ rvalue,
+ data,
+ peer->public_key);
+}
+
+int config_parse_wireguard_allowed_ips(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ union in_addr_union addr;
+ unsigned char prefixlen;
+ int r, family;
+ Wireguard *w;
+ WireguardPeer *peer;
+ WireguardIPmask *ipmask;
+
+ assert(rvalue);
+ assert(data);
+
+ w = WIREGUARD(data);
+
+ peer = wireguard_peer_new(w, section_line);
+ if (!peer)
+ return log_oom();
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&rvalue, &word, "," WHITESPACE, 0);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split allowed ips \"%s\" option: %m", rvalue);
+ break;
+ }
+
+ r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Network address is invalid, ignoring assignment: %s", word);
+ return 0;
+ }
+
+ ipmask = new0(WireguardIPmask, 1);
+ if (!ipmask)
+ return log_oom();
+ ipmask->family = family;
+ ipmask->ip.in6 = addr.in6;
+ ipmask->cidr = prefixlen;
+
+ LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
+ }
+
+ return 0;
+}
+
+int config_parse_wireguard_endpoint(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Wireguard *w;
+ WireguardPeer *peer;
+ size_t len;
+ const char *begin, *end = NULL;
+ _cleanup_free_ char *host = NULL, *port = NULL;
+ _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *endpoint = NULL;
+
+ assert(data);
+ assert(rvalue);
+
+ w = WIREGUARD(data);
+
+ assert(w);
+
+ peer = wireguard_peer_new(w, section_line);
+ if (!peer)
+ return log_oom();
+
+ endpoint = new0(WireguardEndpoint, 1);
+ if (!endpoint)
+ return log_oom();
+
+ if (rvalue[0] == '[') {
+ begin = &rvalue[1];
+ end = strchr(rvalue, ']');
+ if (!end) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find matching brace of endpoint, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+ len = end - begin;
+ ++end;
+ if (*end != ':' || !*(end + 1)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue);
+ return 0;
+ }
+ ++end;
+ } else {
+ begin = rvalue;
+ end = strrchr(rvalue, ':');
+ if (!end || !*(end + 1)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue);
+ return 0;
+ }
+ len = end - begin;
+ ++end;
+ }
+
+ host = strndup(begin, len);
+ if (!host)
+ return log_oom();
+
+ port = strdup(end);
+ if (!port)
+ return log_oom();
+
+ endpoint->peer = peer;
+ endpoint->host = host;
+ endpoint->port = port;
+ endpoint->netdev = netdev_ref(data);
+ LIST_PREPEND(endpoints, w->unresolved_endpoints, endpoint);
+
+ peer = NULL;
+ host = NULL;
+ port = NULL;
+ endpoint = NULL;
+
+ return 0;
+}
+
+int config_parse_wireguard_keepalive(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ int r;
+ uint16_t keepalive = 0;
+ Wireguard *w;
+ WireguardPeer *peer;
+
+ assert(rvalue);
+ assert(data);
+
+ w = WIREGUARD(data);
+
+ assert(w);
+
+ peer = wireguard_peer_new(w, section_line);
+ if (!peer)
+ return log_oom();
+
+ if (streq(rvalue, "off"))
+ keepalive = 0;
+ else {
+ r = safe_atou16(rvalue, &keepalive);
+ if (r < 0)
+ log_syntax(unit, LOG_ERR, filename, line, r, "The persistent keepalive interval must be 0-65535. Ignore assignment: %s", rvalue);
+ }
+
+ peer->persistent_keepalive_interval = keepalive;
+ return 0;
+}
+
+static void wireguard_init(NetDev *netdev) {
+ Wireguard *w;
+
+ assert(netdev);
+
+ w = WIREGUARD(netdev);
+
+ assert(w);
+
+ w->flags = WGDEVICE_F_REPLACE_PEERS;
+}
+
+static void wireguard_done(NetDev *netdev) {
+ Wireguard *w;
+ WireguardPeer *peer;
+ WireguardIPmask *mask;
+
+ assert(netdev);
+ w = WIREGUARD(netdev);
+ assert(!w->unresolved_endpoints);
+ w->resolve_retry_event_source = sd_event_source_unref(w->resolve_retry_event_source);
+
+ while ((peer = w->peers)) {
+ LIST_REMOVE(peers, w->peers, peer);
+ while ((mask = peer->ipmasks)) {
+ LIST_REMOVE(ipmasks, peer->ipmasks, mask);
+ free(mask);
+ }
+ free(peer);
+ }
+}
+
+const NetDevVTable wireguard_vtable = {
+ .object_size = sizeof(Wireguard),
+ .sections = "Match\0NetDev\0WireGuard\0WireGuardPeer\0",
+ .post_create = netdev_wireguard_post_create,
+ .init = wireguard_init,
+ .done = wireguard_done,
+ .create_type = NETDEV_CREATE_INDEPENDENT,
+};
diff --git a/src/network/netdev/wireguard.h b/src/network/netdev/wireguard.h
new file mode 100644
index 0000000000..f788fa4221
--- /dev/null
+++ b/src/network/netdev/wireguard.h
@@ -0,0 +1,98 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Jörg Thalheim <joerg@thalheim.io>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Wireguard Wireguard;
+
+#include "netdev.h"
+#include "sd-resolve.h"
+#include "wireguard-netlink.h"
+#include "socket-util.h"
+#include "in-addr-util.h"
+
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+
+typedef struct WireguardIPmask {
+ uint16_t family;
+ union in_addr_union ip;
+ uint8_t cidr;
+
+ LIST_FIELDS(struct WireguardIPmask, ipmasks);
+} WireguardIPmask;
+
+typedef struct WireguardPeer {
+ uint8_t public_key[WG_KEY_LEN];
+ uint8_t preshared_key[WG_KEY_LEN];
+ uint32_t flags;
+
+ union sockaddr_union endpoint;
+
+ uint16_t persistent_keepalive_interval;
+
+ LIST_HEAD(WireguardIPmask, ipmasks);
+ LIST_FIELDS(struct WireguardPeer, peers);
+} WireguardPeer;
+
+typedef struct WireguardEndpoint {
+ char *host;
+ char *port;
+
+ NetDev *netdev;
+ WireguardPeer *peer;
+
+ LIST_FIELDS(struct WireguardEndpoint, endpoints);
+} WireguardEndpoint;
+
+struct Wireguard {
+ NetDev meta;
+ unsigned last_peer_section;
+
+ char interface[IFNAMSIZ];
+ uint32_t flags;
+
+ uint8_t public_key[WG_KEY_LEN];
+ uint8_t private_key[WG_KEY_LEN];
+ uint32_t fwmark;
+
+ uint16_t port;
+
+ LIST_HEAD(WireguardPeer, peers);
+ size_t allocation_size;
+ sd_event_source *resolve_retry_event_source;
+
+ LIST_HEAD(WireguardEndpoint, unresolved_endpoints);
+ LIST_HEAD(WireguardEndpoint, failed_endpoints);
+ unsigned n_retries;
+ sd_resolve_query *resolve_query;
+};
+
+DEFINE_NETDEV_CAST(WIREGUARD, Wireguard);
+extern const NetDevVTable wireguard_vtable;
+
+int config_parse_wireguard_allowed_ips(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_wireguard_endpoint(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_wireguard_listen_port(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+
+int config_parse_wireguard_public_key(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_wireguard_private_key(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_wireguard_preshared_key(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_wireguard_keepalive(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index ff125e35de..ca5b54bdbf 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -984,251 +984,3 @@ bool address_is_ready(const Address *a) {
else
return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED));
}
-
-int config_parse_router_preference(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
- Network *network = userdata;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (streq(rvalue, "high"))
- network->router_preference = SD_NDISC_PREFERENCE_HIGH;
- else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
- network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
- else if (streq(rvalue, "low"))
- network->router_preference = SD_NDISC_PREFERENCE_LOW;
- else
- log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue);
-
- return 0;
-}
-
-void prefix_free(Prefix *prefix) {
- if (!prefix)
- return;
-
- if (prefix->network) {
- LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix);
- assert(prefix->network->n_static_prefixes > 0);
- prefix->network->n_static_prefixes--;
-
- if (prefix->section)
- hashmap_remove(prefix->network->prefixes_by_section,
- prefix->section);
- }
-
- prefix->radv_prefix = sd_radv_prefix_unref(prefix->radv_prefix);
-
- free(prefix);
-}
-
-int prefix_new(Prefix **ret) {
- _cleanup_prefix_free_ Prefix *prefix = NULL;
-
- prefix = new0(Prefix, 1);
- if (!prefix)
- return -ENOMEM;
-
- if (sd_radv_prefix_new(&prefix->radv_prefix) < 0)
- return -ENOMEM;
-
- *ret = prefix;
- prefix = NULL;
-
- return 0;
-}
-
-int prefix_new_static(Network *network, const char *filename,
- unsigned section_line, Prefix **ret) {
- _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
- _cleanup_prefix_free_ Prefix *prefix = NULL;
- int r;
-
- assert(network);
- assert(ret);
- assert(!!filename == (section_line > 0));
-
- if (filename) {
- r = network_config_section_new(filename, section_line, &n);
- if (r < 0)
- return r;
-
- if (section_line) {
- prefix = hashmap_get(network->prefixes_by_section, n);
- if (prefix) {
- *ret = prefix;
- prefix = NULL;
-
- return 0;
- }
- }
- }
-
- r = prefix_new(&prefix);
- if (r < 0)
- return r;
-
- if (filename) {
- prefix->section = n;
- n = NULL;
-
- r = hashmap_put(network->prefixes_by_section, prefix->section,
- prefix);
- if (r < 0)
- return r;
- }
-
- prefix->network = network;
- LIST_APPEND(prefixes, network->static_prefixes, prefix);
- network->n_static_prefixes++;
-
- *ret = prefix;
- prefix = NULL;
-
- return 0;
-}
-
-int config_parse_prefix(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *network = userdata;
- _cleanup_prefix_free_ Prefix *p = NULL;
- uint8_t prefixlen = 64;
- union in_addr_union in6addr;
- int r;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = prefix_new_static(network, filename, section_line, &p);
- if (r < 0)
- return r;
-
- r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue);
- return 0;
- }
-
- if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0)
- return -EADDRNOTAVAIL;
-
- log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue);
-
- p = NULL;
-
- return 0;
-}
-
-int config_parse_prefix_flags(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
- Network *network = userdata;
- _cleanup_prefix_free_ Prefix *p = NULL;
- int r, val;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = prefix_new_static(network, filename, section_line, &p);
- if (r < 0)
- return r;
-
- r = parse_boolean(rvalue);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue);
- return 0;
- }
-
- val = r;
-
- if (streq(lvalue, "OnLink"))
- r = sd_radv_prefix_set_onlink(p->radv_prefix, val);
- else if (streq(lvalue, "AddressAutoconfiguration"))
- r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val);
- if (r < 0)
- return r;
-
- p = NULL;
-
- return 0;
-}
-
-int config_parse_prefix_lifetime(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
- Network *network = userdata;
- _cleanup_prefix_free_ Prefix *p = NULL;
- usec_t usec;
- int r;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = prefix_new_static(network, filename, section_line, &p);
- if (r < 0)
- return r;
-
- r = parse_sec(rvalue, &usec);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
- return 0;
- }
-
- /* a value of 0xffffffff represents infinity */
- if (streq(lvalue, "PreferredLifetimeSec"))
- r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix,
- DIV_ROUND_UP(usec, USEC_PER_SEC));
- else if (streq(lvalue, "ValidLifetimeSec"))
- r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix,
- DIV_ROUND_UP(usec, USEC_PER_SEC));
- if (r < 0)
- return r;
-
- p = NULL;
-
- return 0;
-}
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index 3f5dffac4c..c2a241b571 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -26,7 +26,6 @@
#include "in-addr-util.h"
typedef struct Address Address;
-typedef struct Prefix Prefix;
#include "networkd-link.h"
#include "networkd-network.h"
@@ -37,15 +36,6 @@ typedef struct Network Network;
typedef struct Link Link;
typedef struct NetworkConfigSection NetworkConfigSection;
-struct Prefix {
- Network *network;
- NetworkConfigSection *section;
-
- sd_radv_prefix *radv_prefix;
-
- LIST_FIELDS(Prefix, prefixes);
-};
-
struct Address {
Network *network;
NetworkConfigSection *section;
@@ -90,21 +80,9 @@ bool address_is_ready(const Address *a);
DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free);
#define _cleanup_address_free_ _cleanup_(address_freep)
-int prefix_new(Prefix **ret);
-void prefix_free(Prefix *prefix);
-int prefix_new_static(Network *network, const char *filename, unsigned section,
- Prefix **ret);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Prefix*, prefix_free);
-#define _cleanup_prefix_free_ _cleanup_(prefix_freep)
-
int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_broadcast(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_address_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_address_scope(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_router_preference(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_prefix(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_prefix_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_prefix_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 0b46deb009..ecb96cdb57 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -71,8 +71,9 @@ static int route_scope_from_address(const Route *route, const struct in_addr *se
}
static int link_set_dhcp_routes(Link *link) {
- struct in_addr gateway, address;
_cleanup_free_ sd_dhcp_route **static_routes = NULL;
+ bool classless_route = false, static_route = false;
+ struct in_addr gateway, address;
int r, n, i;
uint32_t table;
@@ -102,8 +103,21 @@ static int link_set_dhcp_routes(Link *link) {
log_link_debug_errno(link, n, "DHCP error: could not get routes: %m");
for (i = 0; i < n; i++) {
+ if (static_routes[i]->option == SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE)
+ classless_route = true;
+
+ if (static_routes[i]->option == SD_DHCP_OPTION_STATIC_ROUTE)
+ static_route = true;
+ }
+
+ for (i = 0; i < n; i++) {
_cleanup_route_free_ Route *route = NULL;
+ /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option,
+ the DHCP client MUST ignore the Static Routes option. */
+ if (classless_route && static_routes[i]->option == SD_DHCP_OPTION_STATIC_ROUTE)
+ continue;
+
r = route_new(&route);
if (r < 0)
return log_link_error_errno(link, r, "Could not allocate route: %m");
@@ -132,7 +146,10 @@ static int link_set_dhcp_routes(Link *link) {
/* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
a Router option, the DHCP client MUST ignore the Router option. */
- if (r >= 0 && link->dhcp4_messages <= 0) {
+ if (classless_route && static_route)
+ log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option and router option");
+
+ if (r >= 0 && !classless_route) {
_cleanup_route_free_ Route *route = NULL;
_cleanup_route_free_ Route *route_gw = NULL;
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index a46a11bf16..234e0a4602 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -20,21 +20,249 @@
#include <netinet/ether.h>
#include <linux/if.h>
+#include "sd-radv.h"
#include "sd-dhcp6-client.h"
+#include "hashmap.h"
#include "hostname-util.h"
#include "network-internal.h"
#include "networkd-link.h"
#include "networkd-manager.h"
+#include "siphash24.h"
+#include "string-util.h"
+#include "radv-internal.h"
static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
+static bool dhcp6_verify_link(Link *link) {
+ if (!link->network) {
+ log_link_info(link, "Link is not managed by us");
+ return false;
+ }
+
+ if (!IN_SET(link->network->router_prefix_delegation,
+ RADV_PREFIX_DELEGATION_DHCP6,
+ RADV_PREFIX_DELEGATION_BOTH)) {
+ log_link_debug(link, "Link does not request DHCPv6 prefix delegation");
+ return false;
+ }
+
+ return true;
+}
+
+static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
+ Manager *manager;
+ Link *l;
+ Iterator i;
+
+ assert(dhcp6_link);
+
+ manager = dhcp6_link->manager;
+ assert(manager);
+
+ HASHMAP_FOREACH(l, manager->links, i) {
+ if (l == dhcp6_link)
+ continue;
+
+ if (!dhcp6_verify_link(l))
+ continue;
+
+ return true;
+ }
+
+ return false;
+}
+
static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
Link *link) {
return 0;
}
+static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
+ uint8_t prefix_len,
+ uint32_t lifetime_preferred,
+ uint32_t lifetime_valid) {
+ sd_radv *radv = link->radv;
+ int r;
+ _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
+
+ r = sd_radv_prefix_new(&p);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_stop(radv);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_add_prefix(radv, p, true);
+ if (r < 0 && r != -EEXIST)
+ return r;
+
+ r = manager_dhcp6_prefix_add(link->manager, &p->opt.in6_addr, link);
+ if (r < 0)
+ return r;
+
+ return sd_radv_start(radv);
+}
+
+static Network *dhcp6_reset_pd_prefix_network(Link *link) {
+ assert(link);
+ assert(link->manager);
+ assert(link->manager->networks);
+
+ return link->manager->networks;
+}
+
+static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
+ struct in6_addr *pd_prefix,
+ uint8_t pd_prefix_len,
+ uint32_t lifetime_preferred,
+ uint32_t lifetime_valid) {
+ Link *link;
+ Manager *manager = dhcp6_link->manager;
+ union in_addr_union prefix;
+ uint8_t n_prefixes, n_used = 0;
+ _cleanup_free_ char *buf = NULL;
+ int r;
+
+ assert(manager);
+ assert(pd_prefix_len <= 64);
+
+ prefix.in6 = *pd_prefix;
+
+ r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
+ if (r < 0)
+ return r;
+
+ n_prefixes = 1 << (64 - pd_prefix_len);
+
+ (void) in_addr_to_string(AF_INET6, &prefix, &buf);
+ log_link_debug(dhcp6_link, "Assigning up to %u prefixes from %s/%u",
+ n_prefixes, strnull(buf), pd_prefix_len);
+
+ while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
+ Link *assigned_link;
+
+ if (n_used == n_prefixes) {
+ log_link_debug(dhcp6_link, "Assigned %u/%u prefixes from %s/%u",
+ n_used, n_prefixes, strnull(buf), pd_prefix_len);
+
+ return -EAGAIN;
+ }
+
+ if (link == dhcp6_link)
+ continue;
+
+ if (!dhcp6_verify_link(link))
+ continue;
+
+ assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6);
+ if (assigned_link != NULL && assigned_link != link)
+ continue;
+
+ r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
+ lifetime_preferred, lifetime_valid);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Unable to %s prefix %s/%u for link: %m",
+ assigned_link ? "update": "assign",
+ strnull(buf), pd_prefix_len);
+
+ if (assigned_link == NULL)
+ continue;
+
+ } else
+ log_link_debug(link, "Assigned prefix %u/%u %s/64 to link",
+ n_used + 1, n_prefixes, strnull(buf));
+
+ n_used++;
+
+ r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len);
+ if (r < 0 && n_used < n_prefixes)
+ return r;
+ }
+
+ if (n_used < n_prefixes) {
+ Route *route;
+ int n = n_used;
+
+ r = route_new(&route);
+ if (r < 0)
+ return r;
+
+ while (n < n_prefixes) {
+ route_update(route, &prefix, pd_prefix_len, NULL, NULL,
+ 0, 0, RTN_UNREACHABLE);
+
+ r = route_configure(route, link, NULL);
+ if (r < 0) {
+ route_free(route);
+ return r;
+ }
+
+ r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return n_used;
+}
+
+static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
+ int r;
+ sd_dhcp6_lease *lease;
+ struct in6_addr pd_prefix;
+ uint8_t pd_prefix_len;
+ uint32_t lifetime_preferred, lifetime_valid;
+ _cleanup_free_ char *buf = NULL;
+ Iterator i = ITERATOR_FIRST;
+
+ r = sd_dhcp6_client_get_lease(client, &lease);
+ if (r < 0)
+ return r;
+
+ (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf);
+
+ dhcp6_reset_pd_prefix_network(link);
+ sd_dhcp6_lease_reset_pd_prefix_iter(lease);
+
+ while (sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len,
+ &lifetime_preferred,
+ &lifetime_valid) >= 0) {
+
+ if (pd_prefix_len > 64) {
+ log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
+ strnull(buf), pd_prefix_len);
+ continue;
+ }
+
+ r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix,
+ pd_prefix_len,
+ lifetime_preferred,
+ lifetime_valid);
+ if (r < 0 && r != -EAGAIN)
+ return r;
+
+ if (r >= 0)
+ i = ITERATOR_FIRST;
+ }
+
+ return 0;
+}
+
static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
void *userdata) {
_cleanup_link_unref_ Link *link = userdata;
@@ -139,6 +367,8 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
log_link_warning(link, "DHCPv6 lease lost");
+ (void) manager_dhcp6_prefix_remove_all(link->manager, link);
+
link->dhcp6_configured = false;
break;
@@ -149,6 +379,10 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
return;
}
+ r = dhcp6_lease_pd_prefix_acquired(client, link);
+ if (r < 0)
+ log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
+
_fallthrough_;
case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
r = dhcp6_lease_information_acquired(client, link);
@@ -237,10 +471,11 @@ static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
int dhcp6_configure(Link *link) {
sd_dhcp6_client *client = NULL;
- int r;
const DUID *duid;
+ int r;
assert(link);
+ assert(link->network);
if (link->dhcp6_client)
return 0;
@@ -279,10 +514,22 @@ int dhcp6_configure(Link *link) {
if (r < 0)
goto error;
+ if (link->network->rapid_commit) {
+ r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
+ if (r < 0)
+ goto error;
+ }
+
r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
if (r < 0)
goto error;
+ if (dhcp6_enable_prefix_delegation(link)) {
+ r = sd_dhcp6_client_set_prefix_delegation(client, true);
+ if (r < 0)
+ goto error;
+ }
+
link->dhcp6_client = client;
return 0;
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 60ac980ad9..64c45080df 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -92,6 +92,9 @@ static bool link_ipv4ll_enabled(Link *link) {
if (!link->network)
return false;
+ if (streq_ptr(link->kind, "wireguard"))
+ return false;
+
return link->network->link_local & ADDRESS_FAMILY_IPV4;
}
@@ -107,6 +110,9 @@ static bool link_ipv6ll_enabled(Link *link) {
if (!link->network)
return false;
+ if (streq_ptr(link->kind, "wireguard"))
+ return false;
+
return link->network->link_local & ADDRESS_FAMILY_IPV6;
}
@@ -129,7 +135,7 @@ static bool link_radv_enabled(Link *link) {
if (!link_ipv6ll_enabled(link))
return false;
- return link->network->router_prefix_delegation;
+ return link->network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE;
}
static bool link_lldp_rx_enabled(Link *link) {
@@ -849,6 +855,8 @@ static int link_enter_set_routes(Link *link) {
assert(link->network);
assert(link->state == LINK_STATE_SETTING_ADDRESSES);
+ (void) link_set_routing_policy_rule(link);
+
link_set_state(link, LINK_STATE_SETTING_ROUTES);
LIST_FOREACH(routes, rt, link->network->static_routes) {
@@ -862,8 +870,6 @@ static int link_enter_set_routes(Link *link) {
link->route_messages++;
}
- (void) link_set_routing_policy_rule(link);
-
if (link->route_messages == 0) {
link->static_routes_configured = true;
link_check_ready(link);
@@ -3317,6 +3323,12 @@ int link_update(Link *link, sd_netlink_message *m) {
if (r < 0)
return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m");
}
+
+ if (link->ndisc) {
+ r = sd_ndisc_set_mac(link->ndisc, &link->mac);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update MAC for ndisc: %m");
+ }
}
}
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index cc17af9391..749b87f336 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -82,19 +82,6 @@ static int setup_default_address_pool(Manager *m) {
return 0;
}
-static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
- Manager *m = userdata;
-
- assert(s);
- assert(m);
-
- m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
-
- manager_connect_bus(m);
-
- return 0;
-}
-
static int manager_reset_all(Manager *m) {
Link *link;
Iterator i;
@@ -116,6 +103,7 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b
int b, r;
assert(message);
+ assert(m);
r = sd_bus_message_read(message, "b", &b);
if (r < 0) {
@@ -128,40 +116,37 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b
log_debug("Coming back from suspend, resetting all connections...");
- manager_reset_all(m);
+ (void) manager_reset_all(m);
return 0;
}
-int manager_connect_bus(Manager *m) {
- int r;
+static int on_connected(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
+ Manager *m = userdata;
+ assert(message);
assert(m);
- r = sd_bus_default_system(&m->bus);
- if (r < 0) {
- /* We failed to connect? Yuck, we must be in early
- * boot. Let's try in 5s again. */
+ /* Did we get a timezone or transient hostname from DHCP while D-Bus wasn't up yet? */
+ if (m->dynamic_hostname)
+ (void) manager_set_hostname(m, m->dynamic_hostname);
+ if (m->dynamic_timezone)
+ (void) manager_set_timezone(m, m->dynamic_timezone);
+
+ return 0;
+}
- log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m");
+int manager_connect_bus(Manager *m) {
+ int r;
- r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m);
- if (r < 0)
- return log_error_errno(r, "Failed to install bus reconnect time event: %m");
+ assert(m);
+ if (m->bus)
return 0;
- }
- r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot,
- "type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Manager',"
- "member='PrepareForSleep',"
- "path='/org/freedesktop/login1'",
- match_prepare_for_sleep,
- m);
+ r = bus_open_system_watch_bind(&m->bus);
if (r < 0)
- return log_error_errno(r, "Failed to add match for PrepareForSleep: %m");
+ return log_error_errno(r, "Failed to connect to bus: %m");
r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/network1", "org.freedesktop.network1.Manager", manager_vtable, m);
if (r < 0)
@@ -183,25 +168,35 @@ int manager_connect_bus(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to add network enumerator: %m");
- r = sd_bus_request_name(m->bus, "org.freedesktop.network1", 0);
+ r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.network1", 0, NULL, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to register name: %m");
+ return log_error_errno(r, "Failed to request name: %m");
r = sd_bus_attach_event(m->bus, m->event, 0);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
- /* Did we get a timezone or transient hostname from DHCP while D-Bus wasn't up yet? */
- if (m->dynamic_hostname) {
- r = manager_set_hostname(m, m->dynamic_hostname);
- if (r < 0)
- return r;
- }
- if (m->dynamic_timezone) {
- r = manager_set_timezone(m, m->dynamic_timezone);
- if (r < 0)
- return r;
- }
+ r = sd_bus_match_signal_async(
+ m->bus,
+ &m->connected_slot,
+ "org.freedesktop.DBus.Local",
+ NULL,
+ "org.freedesktop.DBus.Local",
+ "Connected",
+ on_connected, NULL, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to request match on Connected signal: %m");
+
+ r = sd_bus_match_signal_async(
+ m->bus,
+ &m->prepare_for_sleep_slot,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "PrepareForSleep",
+ match_prepare_for_sleep, NULL, m);
+ if (r < 0)
+ log_warning_errno(r, "Failed to request match for PrepareForSleep, ignoring: %m");
return 0;
}
@@ -244,7 +239,8 @@ static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t
if (!device)
return -ENOMEM;
- manager_udev_process_link(m, device);
+ (void) manager_udev_process_link(m, device);
+
return 0;
}
@@ -309,17 +305,17 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
- log_warning_errno(r, "rtnl: failed to receive route: %m");
+ log_warning_errno(r, "rtnl: failed to receive route, ignoring: %m");
return 0;
}
r = sd_netlink_message_get_type(message, &type);
if (r < 0) {
- log_warning_errno(r, "rtnl: could not get message type: %m");
+ log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
return 0;
} else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
- log_warning("rtnl: received unexpected message type when processing route");
+ log_warning("rtnl: received unexpected message type when processing route, ignoring");
return 0;
}
@@ -346,7 +342,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
r = sd_rtnl_message_route_get_family(message, &family);
if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
- log_link_warning(link, "rtnl: received address with invalid family, ignoring.");
+ log_link_warning(link, "rtnl: received address with invalid family, ignoring");
return 0;
}
@@ -458,15 +454,17 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
return 0;
}
- route_get(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
+ (void) route_get(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
switch (type) {
case RTM_NEWROUTE:
if (!route) {
/* A route appeared that we did not request */
r = route_add_foreign(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
- if (r < 0)
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to add route, ignoring: %m");
return 0;
+ }
}
route_update(route, &src, src_prefixlen, &gw, &prefsrc, scope, protocol, rt_type);
@@ -506,26 +504,26 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
- log_warning_errno(r, "rtnl: failed to receive address: %m");
+ log_warning_errno(r, "rtnl: failed to receive address, ignoring: %m");
return 0;
}
r = sd_netlink_message_get_type(message, &type);
if (r < 0) {
- log_warning_errno(r, "rtnl: could not get message type: %m");
+ log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
return 0;
} else if (!IN_SET(type, RTM_NEWADDR, RTM_DELADDR)) {
- log_warning("rtnl: received unexpected message type when processing address");
+ log_warning("rtnl: received unexpected message type when processing address, ignoring");
return 0;
}
r = sd_rtnl_message_addr_get_ifindex(message, &ifindex);
if (r < 0) {
- log_warning_errno(r, "rtnl: could not get ifindex from address: %m");
+ log_warning_errno(r, "rtnl: could not get ifindex from address, ignoring: %m");
return 0;
} else if (ifindex <= 0) {
- log_warning("rtnl: received address message with invalid ifindex: %d", ifindex);
+ log_warning("rtnl: received address message with invalid ifindex, ignoring: %d", ifindex);
return 0;
} else {
r = link_get(m, ifindex, &link);
@@ -540,7 +538,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
r = sd_rtnl_message_addr_get_family(message, &family);
if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
- log_link_warning(link, "rtnl: received address with invalid family, ignoring.");
+ log_link_warning(link, "rtnl: received address with invalid family, ignoring");
return 0;
}
@@ -582,23 +580,26 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
break;
default:
- log_link_debug(link, "rtnl: ignoring unsupported address family: %d", family);
+ assert_not_reached("Received unsupported address family");
}
if (!inet_ntop(family, &in_addr, buf, INET6_ADDRSTRLEN)) {
- log_link_warning(link, "Could not print address");
+ log_link_warning(link, "Could not print address, ignoring");
return 0;
}
r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo);
- if (r >= 0) {
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: cannot get IFA_CACHEINFO attribute, ignoring: %m");
+ return 0;
+ } else if (r >= 0) {
if (cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
cinfo.ifa_valid * USEC_PER_SEC,
USEC_PER_SEC);
}
- address_get(link, family, &in_addr, prefixlen, &address);
+ (void) address_get(link, family, &in_addr, prefixlen, &address);
switch (type) {
case RTM_NEWADDR:
@@ -609,14 +610,18 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
/* An address appeared that we did not request */
r = address_add_foreign(link, family, &in_addr, prefixlen, &address);
if (r < 0) {
- log_link_warning_errno(link, r, "Failed to add address %s/%u: %m", buf, prefixlen);
+ log_link_warning_errno(link, r, "Failed to add address %s/%u, ignoring: %m", buf, prefixlen);
return 0;
} else
log_link_debug(link, "Adding address: %s/%u (valid %s%s)", buf, prefixlen,
valid_str ? "for " : "forever", strempty(valid_str));
}
- address_update(address, flags, scope, &cinfo);
+ r = address_update(address, flags, scope, &cinfo);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to update address %s/%u, ignoring: %m", buf, prefixlen);
+ return 0;
+ }
break;
@@ -625,9 +630,9 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
if (address) {
log_link_debug(link, "Removing address: %s/%u (valid %s%s)", buf, prefixlen,
valid_str ? "for " : "forever", strempty(valid_str));
- address_drop(address);
+ (void) address_drop(address);
} else
- log_link_warning(link, "Removing non-existent address: %s/%u (valid %s%s)", buf, prefixlen,
+ log_link_warning(link, "Removing non-existent address: %s/%u (valid %s%s), ignoring", buf, prefixlen,
valid_str ? "for " : "forever", strempty(valid_str));
break;
@@ -653,32 +658,32 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
- log_warning_errno(r, "rtnl: Could not receive link: %m");
+ log_warning_errno(r, "rtnl: Could not receive link, ignoring: %m");
return 0;
}
r = sd_netlink_message_get_type(message, &type);
if (r < 0) {
- log_warning_errno(r, "rtnl: Could not get message type: %m");
+ log_warning_errno(r, "rtnl: Could not get message type, ignoring: %m");
return 0;
} else if (!IN_SET(type, RTM_NEWLINK, RTM_DELLINK)) {
- log_warning("rtnl: Received unexpected message type when processing link");
+ log_warning("rtnl: Received unexpected message type when processing link, ignoring");
return 0;
}
r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
if (r < 0) {
- log_warning_errno(r, "rtnl: Could not get ifindex from link: %m");
+ log_warning_errno(r, "rtnl: Could not get ifindex from link, ignoring: %m");
return 0;
} else if (ifindex <= 0) {
- log_warning("rtnl: received link message with invalid ifindex: %d", ifindex);
+ log_warning("rtnl: received link message with invalid ifindex %d, ignoring", ifindex);
return 0;
}
r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name);
if (r < 0) {
- log_warning_errno(r, "rtnl: Received link message without ifname: %m");
+ log_warning_errno(r, "rtnl: Received link message without ifname, ignoring: %m");
return 0;
}
@@ -691,7 +696,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
/* link is new, so add it */
r = link_add(m, message, &link);
if (r < 0) {
- log_warning_errno(r, "Could not add new link: %m");
+ log_warning_errno(r, "Could not add new link, ignoring: %m");
return 0;
}
}
@@ -700,14 +705,16 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
/* netdev exists, so make sure the ifindex matches */
r = netdev_set_ifindex(netdev, message);
if (r < 0) {
- log_warning_errno(r, "Could not set ifindex on netdev: %m");
+ log_warning_errno(r, "Could not set ifindex on netdev, ignoring: %m");
return 0;
}
}
r = link_update(link, message);
- if (r < 0)
+ if (r < 0) {
+ log_warning_errno(r, "Could not update link, ignoring: %m");
return 0;
+ }
break;
@@ -726,11 +733,11 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0;
+ union in_addr_union to = {}, from = {};
RoutingPolicyRule *rule = NULL;
- union in_addr_union to, from;
uint32_t fwmark = 0, table = 0;
+ char *iif = NULL, *oif = NULL;
Manager *m = userdata;
- char *iif, *oif;
uint16_t type;
int family;
int r;
@@ -742,60 +749,80 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
- log_warning_errno(r, "rtnl: failed to receive rule: %m");
+ log_warning_errno(r, "rtnl: failed to receive rule, ignoring: %m");
return 0;
}
r = sd_netlink_message_get_type(message, &type);
if (r < 0) {
- log_warning_errno(r, "rtnl: could not get message type: %m");
+ log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
return 0;
} else if (!IN_SET(type, RTM_NEWRULE, RTM_DELRULE)) {
- log_warning("rtnl: received unexpected message type '%u' when processing rule.", type);
+ log_warning("rtnl: received unexpected message type '%u' when processing rule, ignoring", type);
return 0;
}
r = sd_rtnl_message_get_family(message, &family);
if (r < 0) {
- log_warning_errno(r, "rtnl: could not get rule family: %m");
+ log_warning_errno(r, "rtnl: could not get rule family, ignoring: %m");
return 0;
} else if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("rtnl: received address with invalid family %u, ignoring.", family);
+ log_debug("rtnl: received address with invalid family %u, ignoring", family);
return 0;
}
switch (family) {
case AF_INET:
r = sd_netlink_message_read_in_addr(message, FRA_SRC, &from.in);
- if (r >= 0) {
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m");
+ return 0;
+ } else if (r >= 0) {
r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &from_prefixlen);
- if (r < 0)
- log_warning_errno(r, "rtnl: failed to retrive rule from prefix length: %m");
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: failed to retrive rule from prefix length, ignoring: %m");
+ return 0;
+ }
}
r = sd_netlink_message_read_in_addr(message, FRA_DST, &to.in);
- if (r >= 0) {
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m");
+ return 0;
+ } else if (r >= 0) {
r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &to_prefixlen);
- if (r < 0)
- log_warning_errno(r, "rtnl: failed to retrive rule to prefix length: %m");
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: failed to retrive rule to prefix length, ignoring: %m");
+ return 0;
+ }
}
break;
case AF_INET6:
r = sd_netlink_message_read_in6_addr(message, FRA_SRC, &from.in6);
- if (r >= 0) {
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m");
+ return 0;
+ } else if (r >= 0) {
r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &from_prefixlen);
- if (r < 0)
- log_warning_errno(r, "rtnl: failed to retrive rule from prefix length: %m");
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: failed to retrive rule from prefix length, ignoring: %m");
+ return 0;
+ }
}
r = sd_netlink_message_read_in6_addr(message, FRA_DST, &to.in6);
- if (r >= 0) {
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m");
+ return 0;
+ } else if (r >= 0) {
r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &to_prefixlen);
- if (r < 0)
- log_warning_errno(r, "rtnl: failed to retrive rule to prefix length: %m");
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: failed to retrive rule to prefix length, ignoring: %m");
+ return 0;
+ }
}
break;
@@ -807,11 +834,35 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
if (from_prefixlen == 0 && to_prefixlen == 0)
return 0;
- (void) sd_netlink_message_read_u32(message, FRA_FWMARK, &fwmark);
- (void) sd_netlink_message_read_u32(message, FRA_TABLE, &table);
- (void) sd_rtnl_message_routing_policy_rule_get_tos(message, &tos);
- (void) sd_netlink_message_read_string(message, FRA_IIFNAME, (const char **) &iif);
- (void) sd_netlink_message_read_string(message, FRA_OIFNAME, (const char **) &oif);
+ r = sd_netlink_message_read_u32(message, FRA_FWMARK, &fwmark);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_FWMARK attribute, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_u32(message, FRA_TABLE, &table);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_TABLE attribute, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_routing_policy_rule_get_tos(message, &tos);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get ip rule TOS, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_string(message, FRA_IIFNAME, (const char **) &iif);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_IIFNAME attribute, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_string(message, FRA_OIFNAME, (const char **) &oif);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_OIFNAME attribute, ignoring: %m");
+ return 0;
+ }
(void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, &rule);
@@ -820,7 +871,7 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
if (!rule) {
r = routing_policy_rule_add_foreign(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, &rule);
if (r < 0) {
- log_warning_errno(r, "Could not add rule: %m");
+ log_warning_errno(r, "Could not add rule, ignoring: %m");
return 0;
}
}
@@ -856,6 +907,26 @@ static int systemd_netlink_fd(void) {
return rtnl_fd;
}
+static int manager_connect_genl(Manager *m) {
+ int r;
+
+ assert(m);
+
+ r = sd_genl_socket_open(&m->genl);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_inc_rcvbuf(m->genl, RCVBUF_SIZE);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_attach_event(m->genl, m->event, 0);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
static int manager_connect_rtnl(Manager *m) {
int fd, r;
@@ -1178,6 +1249,145 @@ static int manager_dirty_handler(sd_event_source *s, void *userdata) {
return 1;
}
+Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
+ assert_return(m, NULL);
+ assert_return(m->dhcp6_prefixes, NULL);
+ assert_return(addr, NULL);
+
+ return hashmap_get(m->dhcp6_prefixes, addr);
+}
+
+static int dhcp6_route_add_callback(sd_netlink *nl, sd_netlink_message *m,
+ void *userdata) {
+ Link *l = userdata;
+ int r;
+ union in_addr_union prefix;
+ _cleanup_free_ char *buf = NULL;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r != 0) {
+ log_link_debug_errno(l, r, "Received error adding DHCPv6 Prefix Delegation route: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in6_addr(m, RTA_DST, &prefix.in6);
+ if (r < 0) {
+ log_link_debug_errno(l, r, "Could not read IPv6 address from DHCPv6 Prefix Delegation while adding route: %m");
+ return 0;
+ }
+
+ (void) in_addr_to_string(AF_INET6, &prefix, &buf);
+ log_link_debug(l, "Added DHCPv6 Prefix Deleagtion route %s/64",
+ strnull(buf));
+
+ return 0;
+}
+
+int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
+ int r;
+ Route *route;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->dhcp6_prefixes, -ENODATA);
+ assert_return(addr, -EINVAL);
+
+ r = route_add(link, AF_INET6, (union in_addr_union *) addr, 64,
+ 0, 0, 0, &route);
+ if (r < 0)
+ return r;
+
+ r = route_configure(route, link, dhcp6_route_add_callback);
+ if (r < 0)
+ return r;
+
+ return hashmap_put(m->dhcp6_prefixes, addr, link);
+}
+
+static int dhcp6_route_remove_callback(sd_netlink *nl, sd_netlink_message *m,
+ void *userdata) {
+ Link *l = userdata;
+ int r;
+ union in_addr_union prefix;
+ _cleanup_free_ char *buf = NULL;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r != 0) {
+ log_link_debug_errno(l, r, "Received error on DHCPv6 Prefix Delegation route removal: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in6_addr(m, RTA_DST, &prefix.in6);
+ if (r < 0) {
+ log_link_debug_errno(l, r, "Could not read IPv6 address from DHCPv6 Prefix Delegation while removing route: %m");
+ return 0;
+ }
+
+ (void) in_addr_to_string(AF_INET6, &prefix, &buf);
+ log_link_debug(l, "Removed DHCPv6 Prefix Delegation route %s/64",
+ strnull(buf));
+
+ return 0;
+}
+
+int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
+ Link *l;
+ int r;
+ Route *route;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->dhcp6_prefixes, -ENODATA);
+ assert_return(addr, -EINVAL);
+
+ l = hashmap_remove(m->dhcp6_prefixes, addr);
+ if (!l)
+ return -EINVAL;
+
+ (void) sd_radv_remove_prefix(l->radv, addr, 64);
+ r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64,
+ 0, 0, 0, &route);
+ if (r >= 0)
+ (void) route_remove(route, l, dhcp6_route_remove_callback);
+
+ return 0;
+}
+
+int manager_dhcp6_prefix_remove_all(Manager *m, Link *link) {
+ Iterator i;
+ Link *l;
+ struct in6_addr *addr;
+
+ assert_return(m, -EINVAL);
+ assert_return(link, -EINVAL);
+
+ HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i) {
+ if (l != link)
+ continue;
+
+ (void) manager_dhcp6_prefix_remove(m, addr);
+ }
+
+ return 0;
+}
+
+static void dhcp6_prefixes_hash_func(const void *p, struct siphash *state) {
+ const struct in6_addr *addr = p;
+
+ assert(p);
+
+ siphash24_compress(addr, sizeof(*addr), state);
+}
+
+static int dhcp6_prefixes_compare_func(const void *_a, const void *_b) {
+ const struct in6_addr *a = _a, *b = _b;
+
+ return memcmp(&a, &b, sizeof(*a));
+}
+
+static const struct hash_ops dhcp6_prefixes_hash_ops = {
+ .hash = dhcp6_prefixes_hash_func,
+ .compare = dhcp6_prefixes_compare_func,
+};
+
int manager_new(Manager **ret, sd_event *event) {
_cleanup_manager_free_ Manager *m = NULL;
int r;
@@ -1200,6 +1410,10 @@ int manager_new(Manager **ret, sd_event *event) {
if (r < 0)
return r;
+ r = manager_connect_genl(m);
+ if (r < 0)
+ return r;
+
r = manager_connect_udev(m);
if (r < 0)
return r;
@@ -1210,10 +1424,22 @@ int manager_new(Manager **ret, sd_event *event) {
LIST_HEAD_INIT(m->networks);
+ r = sd_resolve_default(&m->resolve);
+ if (r < 0)
+ return r;
+
+ r = sd_resolve_attach_event(m->resolve, m->event, 0);
+ if (r < 0)
+ return r;
+
r = setup_default_address_pool(m);
if (r < 0)
return r;
+ m->dhcp6_prefixes = hashmap_new(&dhcp6_prefixes_hash_ops);
+ if (!m->dhcp6_prefixes)
+ return -ENOMEM;
+
m->duid.type = DUID_TYPE_EN;
(void) routing_policy_load_rules(m->state_file, &m->rules_saved);
@@ -1238,6 +1464,10 @@ void manager_free(Manager *m) {
while ((network = m->networks))
network_free(network);
+ while ((link = hashmap_first(m->dhcp6_prefixes)))
+ link_unref(link);
+ hashmap_free(m->dhcp6_prefixes);
+
while ((link = hashmap_first(m->links)))
link_unref(link);
hashmap_free(m->links);
@@ -1259,12 +1489,15 @@ void manager_free(Manager *m) {
sd_netlink_unref(m->rtnl);
sd_event_unref(m->event);
+ sd_resolve_unref(m->resolve);
+
sd_event_source_unref(m->udev_event_source);
udev_monitor_unref(m->udev_monitor);
udev_unref(m->udev);
sd_bus_unref(m->bus);
sd_bus_slot_unref(m->prepare_for_sleep_slot);
+ sd_bus_slot_unref(m->connected_slot);
sd_event_source_unref(m->bus_retry_event_source);
free(m->dynamic_timezone);
@@ -1539,12 +1772,12 @@ int manager_set_hostname(Manager *m, const char *hostname) {
int r;
log_debug("Setting transient hostname: '%s'", strna(hostname));
+
if (free_and_strdup(&m->dynamic_hostname, hostname) < 0)
return log_oom();
- if (!m->bus) {
- /* TODO: replace by assert when we can rely on kdbus */
- log_info("Not connected to system bus, ignoring transient hostname.");
+ if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
+ log_info("Not connected to system bus, not setting hostname.");
return 0;
}
@@ -1591,8 +1824,8 @@ int manager_set_timezone(Manager *m, const char *tz) {
if (free_and_strdup(&m->dynamic_timezone, tz) < 0)
return log_oom();
- if (!m->bus) {
- log_info("Not connected to system bus, ignoring timezone.");
+ if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
+ log_info("Not connected to system bus, not setting hostname.");
return 0;
}
diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h
index 186cb41891..229812ae0e 100644
--- a/src/network/networkd-manager.h
+++ b/src/network/networkd-manager.h
@@ -25,6 +25,7 @@
#include "sd-bus.h"
#include "sd-event.h"
#include "sd-netlink.h"
+#include "sd-resolve.h"
#include "udev.h"
#include "dhcp-identifier.h"
@@ -39,10 +40,14 @@ extern const char* const network_dirs[];
struct Manager {
sd_netlink *rtnl;
+ /* lazy initialized */
+ sd_netlink *genl;
sd_event *event;
+ sd_resolve *resolve;
sd_event_source *bus_retry_event_source;
sd_bus *bus;
sd_bus_slot *prepare_for_sleep_slot;
+ sd_bus_slot *connected_slot;
struct udev *udev;
struct udev_monitor *udev_monitor;
sd_event_source *udev_event_source;
@@ -58,6 +63,7 @@ struct Manager {
Hashmap *links;
Hashmap *netdevs;
Hashmap *networks_by_name;
+ Hashmap *dhcp6_prefixes;
LIST_HEAD(Network, networks);
LIST_HEAD(AddressPool, address_pools);
@@ -109,5 +115,10 @@ Link* manager_find_uplink(Manager *m, Link *exclude);
int manager_set_hostname(Manager *m, const char *hostname);
int manager_set_timezone(Manager *m, const char *timezone);
+Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr);
+int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link);
+int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr);
+int manager_dhcp6_prefix_remove_all(Manager *m, Link *link);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
#define _cleanup_manager_free_ _cleanup_(manager_freep)
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 57a96aff94..281ba657b3 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -27,7 +27,8 @@ Match.Type, config_parse_strv,
Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name)
Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, match_host)
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel_cmdline)
+Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, match_kernel_version)
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch)
Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu)
@@ -107,6 +108,9 @@ Route.GatewayOnlink, config_parse_gateway_onlink,
Route.IPv6Preference, config_parse_ipv6_route_preference, 0, 0
Route.Protocol, config_parse_route_protocol, 0, 0
Route.Type, config_parse_route_type, 0, 0
+Route.InitialCongestionWindow, config_parse_tcp_window, 0, 0
+Route.InitialAdvertisedReceiveWindow, config_parse_tcp_window, 0, 0
+Route.QuickAck, config_parse_quickack, 0, 0
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp)
@@ -127,6 +131,7 @@ DHCP.RouteTable, config_parse_dhcp_route_table,
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid)
DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
+DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
IPv6AcceptRA.RouteTable, config_parse_uint32, 0, offsetof(Network, ipv6_accept_ra_route_table)
@@ -153,7 +158,7 @@ BridgeFDB.VLANId, config_parse_fdb_vlan_id,
BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0
BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0
BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0
-Network.IPv6PrefixDelegation, config_parse_bool, 0, offsetof(Network, router_prefix_delegation)
+Network.IPv6PrefixDelegation, config_parse_router_prefix_delegation, 0, 0
IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec)
IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 8e37a0a229..2dc3de3f6a 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -228,6 +228,7 @@ static int network_load_one(Manager *manager, const char *filename) {
network->dhcp_use_mtu = false;
/* NOTE: from man: UseTimezone=... Defaults to "no".*/
network->dhcp_use_timezone = false;
+ network->rapid_commit = true;
network->dhcp_server_emit_dns = true;
network->dhcp_server_emit_ntp = true;
@@ -431,7 +432,8 @@ void network_free(Network *network) {
condition_free_list(network->match_host);
condition_free_list(network->match_virt);
- condition_free_list(network->match_kernel);
+ condition_free_list(network->match_kernel_cmdline);
+ condition_free_list(network->match_kernel_version);
condition_free_list(network->match_arch);
free(network->dhcp_server_timezone);
@@ -485,8 +487,8 @@ int network_get(Manager *manager, struct udev_device *device,
if (net_match_config(network->match_mac, network->match_path,
network->match_driver, network->match_type,
network->match_name, network->match_host,
- network->match_virt, network->match_kernel,
- network->match_arch,
+ network->match_virt, network->match_kernel_cmdline,
+ network->match_kernel_version, network->match_arch,
address, path, parent_driver, driver,
devtype, ifname)) {
if (network->match_name && device) {
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 49c62654b6..7b40ba51e6 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -34,6 +34,7 @@
#include "networkd-fdb.h"
#include "networkd-lldp-tx.h"
#include "networkd-ipv6-proxy-ndp.h"
+#include "networkd-radv.h"
#include "networkd-route.h"
#include "networkd-routing-policy-rule.h"
#include "networkd-util.h"
@@ -85,6 +86,13 @@ typedef struct DUID {
uint8_t raw_data[MAX_DUID_LEN];
} DUID;
+typedef enum RADVPrefixDelegation {
+ RADV_PREFIX_DELEGATION_NONE,
+ RADV_PREFIX_DELEGATION_STATIC,
+ RADV_PREFIX_DELEGATION_DHCP6,
+ RADV_PREFIX_DELEGATION_BOTH,
+} RADVPrefixDelegation;
+
typedef struct NetworkConfigSection {
unsigned line;
char filename[];
@@ -112,7 +120,8 @@ struct Network {
Condition *match_host;
Condition *match_virt;
- Condition *match_kernel;
+ Condition *match_kernel_cmdline;
+ Condition *match_kernel_version;
Condition *match_arch;
char *description;
@@ -139,6 +148,7 @@ struct Network {
bool dhcp_use_mtu;
bool dhcp_use_routes;
bool dhcp_use_timezone;
+ bool rapid_commit;
bool dhcp_use_hostname;
bool dhcp_route_table_set;
DHCPUseDomains dhcp_use_domains;
@@ -163,7 +173,7 @@ struct Network {
bool ipv4ll_route;
/* IPv6 prefix delegation support */
- bool router_prefix_delegation;
+ RADVPrefixDelegation router_prefix_delegation;
usec_t router_lifetime_usec;
uint8_t router_preference;
bool router_managed;
diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c
index 535454c472..a921d120b3 100644
--- a/src/network/networkd-radv.c
+++ b/src/network/networkd-radv.c
@@ -24,7 +24,296 @@
#include "networkd-address.h"
#include "networkd-manager.h"
#include "networkd-radv.h"
+#include "parse-util.h"
#include "sd-radv.h"
+#include "string-util.h"
+
+int config_parse_router_prefix_delegation(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ int d;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (streq(rvalue, "static"))
+ network->router_prefix_delegation = RADV_PREFIX_DELEGATION_STATIC;
+ else if (streq(rvalue, "dhcpv6"))
+ network->router_prefix_delegation = RADV_PREFIX_DELEGATION_DHCP6;
+ else {
+ d = parse_boolean(rvalue);
+ if (d > 0)
+ network->router_prefix_delegation = RADV_PREFIX_DELEGATION_BOTH;
+ else
+ network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE;
+
+ if (d < 0)
+ log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router prefix delegation '%s' is invalid, ignoring assignment: %m", rvalue);
+ }
+
+ return 0;
+}
+
+int config_parse_router_preference(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Network *network = userdata;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (streq(rvalue, "high"))
+ network->router_preference = SD_NDISC_PREFERENCE_HIGH;
+ else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
+ network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
+ else if (streq(rvalue, "low"))
+ network->router_preference = SD_NDISC_PREFERENCE_LOW;
+ else
+ log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue);
+
+ return 0;
+}
+
+void prefix_free(Prefix *prefix) {
+ if (!prefix)
+ return;
+
+ if (prefix->network) {
+ LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix);
+ assert(prefix->network->n_static_prefixes > 0);
+ prefix->network->n_static_prefixes--;
+
+ if (prefix->section)
+ hashmap_remove(prefix->network->prefixes_by_section,
+ prefix->section);
+ }
+
+ prefix->radv_prefix = sd_radv_prefix_unref(prefix->radv_prefix);
+
+ free(prefix);
+}
+
+int prefix_new(Prefix **ret) {
+ Prefix *prefix = NULL;
+
+ prefix = new0(Prefix, 1);
+ if (!prefix)
+ return -ENOMEM;
+
+ if (sd_radv_prefix_new(&prefix->radv_prefix) < 0)
+ return -ENOMEM;
+
+ *ret = prefix;
+ prefix = NULL;
+
+ return 0;
+}
+
+int prefix_new_static(Network *network, const char *filename,
+ unsigned section_line, Prefix **ret) {
+ _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
+ _cleanup_prefix_free_ Prefix *prefix = NULL;
+ int r;
+
+ assert(network);
+ assert(ret);
+ assert(!!filename == (section_line > 0));
+
+ if (filename) {
+ r = network_config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ if (section_line) {
+ prefix = hashmap_get(network->prefixes_by_section, n);
+ if (prefix) {
+ *ret = prefix;
+ prefix = NULL;
+
+ return 0;
+ }
+ }
+ }
+
+ r = prefix_new(&prefix);
+ if (r < 0)
+ return r;
+
+ if (filename) {
+ prefix->section = n;
+ n = NULL;
+
+ r = hashmap_put(network->prefixes_by_section, prefix->section,
+ prefix);
+ if (r < 0)
+ return r;
+ }
+
+ prefix->network = network;
+ LIST_APPEND(prefixes, network->static_prefixes, prefix);
+ network->n_static_prefixes++;
+
+ *ret = prefix;
+ prefix = NULL;
+
+ return 0;
+}
+
+int config_parse_prefix(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ _cleanup_prefix_free_ Prefix *p = NULL;
+ uint8_t prefixlen = 64;
+ union in_addr_union in6addr;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = prefix_new_static(network, filename, section_line, &p);
+ if (r < 0)
+ return r;
+
+ r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0)
+ return -EADDRNOTAVAIL;
+
+ log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue);
+
+ p = NULL;
+
+ return 0;
+}
+
+int config_parse_prefix_flags(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Network *network = userdata;
+ _cleanup_prefix_free_ Prefix *p = NULL;
+ int r, val;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = prefix_new_static(network, filename, section_line, &p);
+ if (r < 0)
+ return r;
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ val = r;
+
+ if (streq(lvalue, "OnLink"))
+ r = sd_radv_prefix_set_onlink(p->radv_prefix, val);
+ else if (streq(lvalue, "AddressAutoconfiguration"))
+ r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val);
+ if (r < 0)
+ return r;
+
+ p = NULL;
+
+ return 0;
+}
+
+int config_parse_prefix_lifetime(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Network *network = userdata;
+ _cleanup_prefix_free_ Prefix *p = NULL;
+ usec_t usec;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = prefix_new_static(network, filename, section_line, &p);
+ if (r < 0)
+ return r;
+
+ r = parse_sec(rvalue, &usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ /* a value of 0xffffffff represents infinity */
+ if (streq(lvalue, "PreferredLifetimeSec"))
+ r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix,
+ DIV_ROUND_UP(usec, USEC_PER_SEC));
+ else if (streq(lvalue, "ValidLifetimeSec"))
+ r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix,
+ DIV_ROUND_UP(usec, USEC_PER_SEC));
+ if (r < 0)
+ return r;
+
+ p = NULL;
+
+ return 0;
+}
static int radv_get_ip6dns(Network *network, struct in6_addr **dns,
size_t *n_dns) {
@@ -211,10 +500,14 @@ int radv_configure(Link *link) {
return r;
}
- LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
- r = sd_radv_add_prefix(link->radv, p->radv_prefix);
- if (r != -EEXIST && r < 0)
- return r;
+ if (IN_SET(link->network->router_prefix_delegation,
+ RADV_PREFIX_DELEGATION_STATIC,
+ RADV_PREFIX_DELEGATION_BOTH)) {
+ LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
+ r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
+ if (r != -EEXIST && r < 0)
+ return r;
+ }
}
return radv_emit_dns(link);
diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h
index f23029935a..22a169d263 100644
--- a/src/network/networkd-radv.h
+++ b/src/network/networkd-radv.h
@@ -20,7 +20,33 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "networkd-address.h"
#include "networkd-link.h"
+typedef struct Prefix Prefix;
+
+struct Prefix {
+ Network *network;
+ NetworkConfigSection *section;
+
+ sd_radv_prefix *radv_prefix;
+
+ LIST_FIELDS(Prefix, prefixes);
+};
+
+int prefix_new(Prefix **ret);
+void prefix_free(Prefix *prefix);
+int prefix_new_static(Network *network, const char *filename, unsigned section,
+ Prefix **ret);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Prefix*, prefix_free);
+#define _cleanup_prefix_free_ _cleanup_(prefix_freep)
+
+int config_parse_router_prefix_delegation(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_router_preference(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_prefix(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_prefix_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_prefix_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+
int radv_emit_dns(Link *link);
int radv_configure(Link *link);
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index b0ad707811..70dca5219b 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -74,6 +74,7 @@ int route_new(Route **ret) {
route->type = RTN_UNICAST;
route->table = RT_TABLE_MAIN;
route->lifetime = USEC_INFINITY;
+ route->quickack = -1;
*ret = route;
route = NULL;
@@ -636,6 +637,24 @@ int route_configure(
return log_error_errno(r, "Could not append RTAX_MTU attribute: %m");
}
+ if (route->initcwnd > 0) {
+ r = sd_netlink_message_append_u32(req, RTAX_INITCWND, route->initcwnd);
+ if (r < 0)
+ return log_error_errno(r, "Could not append RTAX_INITCWND attribute: %m");
+ }
+
+ if (route->initrwnd > 0) {
+ r = sd_netlink_message_append_u32(req, RTAX_INITRWND, route->initrwnd);
+ if (r < 0)
+ return log_error_errno(r, "Could not append RTAX_INITRWND attribute: %m");
+ }
+
+ if (route->quickack != -1) {
+ r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack);
+ if (r < 0)
+ return log_error_errno(r, "Could not append RTAX_QUICKACK attribute: %m");
+ }
+
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_error_errno(r, "Could not append RTA_METRICS attribute: %m");
@@ -1069,3 +1088,85 @@ int config_parse_route_type(const char *unit,
return 0;
}
+
+int config_parse_tcp_window(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ _cleanup_route_free_ Route *n = NULL;
+ Network *network = userdata;
+ uint64_t k;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = route_new_static(network, filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = parse_size(rvalue, 1024, &k);
+ if (r < 0 || k > UINT32_MAX) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Could not parse TCP %s \"%s\" bytes, ignoring assignment: %m", rvalue, lvalue);
+ return 0;
+ }
+
+ if (streq(lvalue, "InitialCongestionWindow"))
+ n->initcwnd = k;
+ else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
+ n->initrwnd = k;
+ else {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse TCP %s: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ n = NULL;
+
+ return 0;
+}
+
+int config_parse_quickack(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ _cleanup_route_free_ Route *n = NULL;
+ Network *network = userdata;
+ int k, r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = route_new_static(network, filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ k = parse_boolean(rvalue);
+ if (k < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse TCP quickack, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ n->quickack = !!k;
+ n = NULL;
+
+ return 0;
+}
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index cfb85cbd6d..6db9d592ea 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -32,6 +32,8 @@ struct Route {
Link *link;
int family;
+ int quickack;
+
unsigned char dst_prefixlen;
unsigned char src_prefixlen;
unsigned char scope;
@@ -41,6 +43,8 @@ struct Route {
uint32_t priority; /* note that ip(8) calls this 'metric' */
uint32_t table;
uint32_t mtu;
+ uint32_t initcwnd;
+ uint32_t initrwnd;
unsigned char pref;
unsigned flags;
@@ -81,3 +85,5 @@ int config_parse_gateway_onlink(const char *unit, const char *filename, unsigned
int config_parse_ipv6_route_preference(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_route_protocol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_route_type(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_tcp_window(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_quickack(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c
index 1314564c11..fdbaec58eb 100644
--- a/src/network/networkd-routing-policy-rule.c
+++ b/src/network/networkd-routing-policy-rule.c
@@ -60,10 +60,11 @@ void routing_policy_rule_free(RoutingPolicyRule *rule) {
network_config_section_free(rule->section);
}
- if (rule->network->manager) {
- set_remove(rule->network->manager->rules, rule);
- set_remove(rule->network->manager->rules_foreign, rule);
- }
+ }
+
+ if (rule->manager) {
+ set_remove(rule->manager->rules, rule);
+ set_remove(rule->manager->rules_foreign, rule);
}
free(rule->iif);
@@ -236,7 +237,8 @@ int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule) {
return -ENOENT;
}
-static int routing_policy_rule_add_internal(Set **rules,
+static int routing_policy_rule_add_internal(Manager *m,
+ Set **rules,
int family,
const union in_addr_union *from,
uint8_t from_prefixlen,
@@ -258,6 +260,7 @@ static int routing_policy_rule_add_internal(Set **rules,
if (r < 0)
return r;
+ rule->manager = m;
rule->family = family;
rule->from = *from;
rule->from_prefixlen = from_prefixlen;
@@ -298,7 +301,7 @@ int routing_policy_rule_add(Manager *m,
char *oif,
RoutingPolicyRule **ret) {
- return routing_policy_rule_add_internal(&m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret);
+ return routing_policy_rule_add_internal(m, &m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret);
}
int routing_policy_rule_add_foreign(Manager *m,
@@ -313,7 +316,7 @@ int routing_policy_rule_add_foreign(Manager *m,
char *iif,
char *oif,
RoutingPolicyRule **ret) {
- return routing_policy_rule_add_internal(&m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret);
+ return routing_policy_rule_add_internal(m, &m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret);
}
static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h
index 70a861723b..48353b9d10 100644
--- a/src/network/networkd-routing-policy-rule.h
+++ b/src/network/networkd-routing-policy-rule.h
@@ -33,8 +33,10 @@ typedef struct RoutingPolicyRule RoutingPolicyRule;
typedef struct Network Network;
typedef struct Link Link;
typedef struct NetworkConfigSection NetworkConfigSection;
+typedef struct Manager Manager;
struct RoutingPolicyRule {
+ Manager *manager;
Network *network;
Link *link;
NetworkConfigSection *section;
diff --git a/src/network/networkd.c b/src/network/networkd.c
index 9243384af8..79c15d4111 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -53,24 +53,13 @@ int main(int argc, char *argv[]) {
goto out;
}
- /* Always create the directories people can create inotify
- * watches in. */
+ /* Create runtime directory. This is not necessary when networkd is
+ * started with "RuntimeDirectory=systemd/netif", or after
+ * systemd-tmpfiles-setup.service. */
r = mkdir_safe_label("/run/systemd/netif", 0755, uid, gid, false);
if (r < 0)
log_warning_errno(r, "Could not create runtime directory: %m");
- r = mkdir_safe_label("/run/systemd/netif/links", 0755, uid, gid, false);
- if (r < 0)
- log_warning_errno(r, "Could not create runtime directory 'links': %m");
-
- r = mkdir_safe_label("/run/systemd/netif/leases", 0755, uid, gid, false);
- if (r < 0)
- log_warning_errno(r, "Could not create runtime directory 'leases': %m");
-
- r = mkdir_safe_label("/run/systemd/netif/lldp", 0755, uid, gid, false);
- if (r < 0)
- log_warning_errno(r, "Could not create runtime directory 'lldp': %m");
-
/* Drop privileges, but only if we have been started as root. If we are not running as root we assume all
* privileges are already dropped. */
if (geteuid() == 0) {
@@ -83,6 +72,21 @@ int main(int argc, char *argv[]) {
goto out;
}
+ /* Always create the directories people can create inotify watches in.
+ * It is necessary to create the following subdirectories after drop_privileges()
+ * to support old kernels not supporting AmbientCapabilities=. */
+ r = mkdir_safe_label("/run/systemd/netif/links", 0755, uid, gid, false);
+ if (r < 0)
+ log_warning_errno(r, "Could not create runtime directory 'links': %m");
+
+ r = mkdir_safe_label("/run/systemd/netif/leases", 0755, uid, gid, false);
+ if (r < 0)
+ log_warning_errno(r, "Could not create runtime directory 'leases': %m");
+
+ r = mkdir_safe_label("/run/systemd/netif/lldp", 0755, uid, gid, false);
+ if (r < 0)
+ log_warning_errno(r, "Could not create runtime directory 'lldp': %m");
+
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
r = sd_event_default(&event);
diff --git a/src/network/test-routing-policy-rule.c b/src/network/test-routing-policy-rule.c
index c29d134de2..3bbdd25b76 100644
--- a/src/network/test-routing-policy-rule.c
+++ b/src/network/test-routing-policy-rule.c
@@ -41,13 +41,15 @@ static void test_rule_serialization(const char *title, const char *ruleset, cons
log_info("========== %s ==========", title);
log_info("put:\n%s\n", ruleset);
- assert_se((fd = mkostemp_safe(pattern)) >= 0);
+ fd = mkostemp_safe(pattern);
+ assert_se(fd >= 0);
assert_se(f = fdopen(fd, "a+e"));
assert_se(write_string_stream(f, ruleset, 0) == 0);
assert_se(routing_policy_load_rules(pattern, &rules) == 0);
- assert_se((fd2 = mkostemp_safe(pattern2)) >= 0);
+ fd2 = mkostemp_safe(pattern2);
+ assert_se(fd2 >= 0);
assert_se(f2 = fdopen(fd2, "a+e"));
assert_se(routing_policy_serialize_rules(rules, f2) == 0);
@@ -57,7 +59,8 @@ static void test_rule_serialization(const char *title, const char *ruleset, cons
log_info("got:\n%s", buf);
- assert_se((fd3 = mkostemp_safe(pattern3)) >= 0);
+ fd3 = mkostemp_safe(pattern3);
+ assert_se(fd3 >= 0);
assert_se(f3 = fdopen(fd3, "we"));
assert_se(write_string_stream(f3, expected ?: ruleset, 0) == 0);
diff --git a/src/notify/notify.c b/src/notify/notify.c
index 3e511b7e47..d58a45cdd2 100644
--- a/src/notify/notify.c
+++ b/src/notify/notify.c
@@ -33,12 +33,15 @@
#include "parse-util.h"
#include "string-util.h"
#include "strv.h"
+#include "user-util.h"
#include "util.h"
static bool arg_ready = false;
static pid_t arg_pid = 0;
static const char *arg_status = NULL;
static bool arg_booted = false;
+static uid_t arg_uid = UID_INVALID;
+static gid_t arg_gid = GID_INVALID;
static void help(void) {
printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n\n"
@@ -46,7 +49,8 @@ static void help(void) {
" -h --help Show this help\n"
" --version Show package version\n"
" --ready Inform the init system about service start-up completion\n"
- " --pid[=PID] Set main pid of daemon\n"
+ " --pid[=PID] Set main PID of daemon\n"
+ " --uid=USER Set user to send from\n"
" --status=TEXT Set status text\n"
" --booted Check if the system was booted up with systemd\n",
program_invocation_short_name);
@@ -60,6 +64,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PID,
ARG_STATUS,
ARG_BOOTED,
+ ARG_UID,
};
static const struct option options[] = {
@@ -69,10 +74,11 @@ static int parse_argv(int argc, char *argv[]) {
{ "pid", optional_argument, NULL, ARG_PID },
{ "status", required_argument, NULL, ARG_STATUS },
{ "booted", no_argument, NULL, ARG_BOOTED },
+ { "uid", required_argument, NULL, ARG_UID },
{}
};
- int c;
+ int c, r;
assert(argc >= 0);
assert(argv);
@@ -112,6 +118,18 @@ static int parse_argv(int argc, char *argv[]) {
arg_booted = true;
break;
+ case ARG_UID: {
+ const char *u = optarg;
+
+ r = get_user_creds(&u, &arg_uid, &arg_gid, NULL, NULL);
+ if (r == -ESRCH) /* If the user doesn't exist, then accept it anyway as numeric */
+ r = parse_uid(u, &arg_uid);
+ if (r < 0)
+ return log_error_errno(r, "Can't resolve user %s: %m", optarg);
+
+ break;
+ }
+
case '?':
return -EINVAL;
@@ -179,7 +197,7 @@ int main(int argc, char* argv[]) {
goto finish;
}
- if (strv_length(final_env) <= 0) {
+ if (strv_isempty(final_env)) {
r = 0;
goto finish;
}
@@ -190,6 +208,22 @@ int main(int argc, char* argv[]) {
goto finish;
}
+ /* If this is requested change to the requested UID/GID. Note thta we only change the real UID here, and leave
+ the effective UID in effect (which is 0 for this to work). That's because we want the privileges to fake the
+ ucred data, and sd_pid_notify() uses the real UID for filling in ucred. */
+
+ if (arg_gid != GID_INVALID)
+ if (setregid(arg_gid, (gid_t) -1) < 0) {
+ r = log_error_errno(errno, "Failed to change GID: %m");
+ goto finish;
+ }
+
+ if (arg_uid != UID_INVALID)
+ if (setreuid(arg_uid, (uid_t) -1) < 0) {
+ r = log_error_errno(errno, "Failed to change UID: %m");
+ goto finish;
+ }
+
r = sd_pid_notify(arg_pid ? arg_pid : getppid(), false, n);
if (r < 0) {
log_error_errno(r, "Failed to notify init system: %m");
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
index 920e114718..c9236ea3d1 100644
--- a/src/nspawn/nspawn-mount.c
+++ b/src/nspawn/nspawn-mount.c
@@ -474,9 +474,9 @@ static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, u
assert(path);
- r = mkdir(path, mode);
- if (r < 0 && errno != EEXIST)
- return -errno;
+ r = mkdir_errno_wrapper(path, mode);
+ if (r < 0 && r != -EEXIST)
+ return r;
if ((mask & MOUNT_USE_USERNS) == 0)
return 0;
@@ -484,8 +484,7 @@ static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, u
if (mask & MOUNT_IN_USERNS)
return 0;
- r = lchown(path, uid_shift, uid_shift);
- if (r < 0)
+ if (lchown(path, uid_shift, uid_shift) < 0)
return -errno;
return 0;
diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c
index ef9db31df7..07d68242c6 100644
--- a/src/nspawn/nspawn-register.c
+++ b/src/nspawn/nspawn-register.c
@@ -203,7 +203,7 @@ int register_machine(
if (r < 0)
return r;
- r = bus_append_unit_property_assignment_many(m, properties);
+ r = bus_append_unit_property_assignment_many(m, UNIT_SERVICE, properties);
if (r < 0)
return r;
@@ -339,7 +339,7 @@ int allocate_scope(
if (r < 0)
return r;
- r = bus_append_unit_property_assignment_many(m, properties);
+ r = bus_append_unit_property_assignment_many(m, UNIT_SCOPE, properties);
if (r < 0)
return r;
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
index c0c5a153b0..10b8a63052 100644
--- a/src/nspawn/nspawn-settings.h
+++ b/src/nspawn/nspawn-settings.h
@@ -22,6 +22,8 @@
#include <stdio.h>
+#include "sd-id128.h"
+
#include "macro.h"
#include "nspawn-expose-ports.h"
#include "nspawn-mount.h"
diff --git a/src/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c
index 31f5dd3cdd..b08bcd988a 100644
--- a/src/nspawn/nspawn-setuid.c
+++ b/src/nspawn/nspawn-setuid.c
@@ -23,6 +23,7 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "errno.h"
#include "fd-util.h"
#include "mkdir.h"
#include "nspawn-setuid.h"
@@ -33,7 +34,7 @@
#include "util.h"
static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
- int pipe_fds[2];
+ int pipe_fds[2], r;
pid_t pid;
assert(database);
@@ -43,10 +44,10 @@ static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
if (pipe2(pipe_fds, O_CLOEXEC) < 0)
return log_error_errno(errno, "Failed to allocate pipe: %m");
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork getent child: %m");
- else if (pid == 0) {
+ r = safe_fork("(getent)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
int nullfd;
char *empty_env = NULL;
@@ -71,8 +72,6 @@ static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
if (nullfd > 2)
safe_close(nullfd);
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
close_all_fds(NULL, 0);
execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env);
@@ -135,7 +134,7 @@ int change_uid_gid(const char *user, char **_home) {
truncate_nl(line);
- wait_for_terminate_and_warn("getent passwd", pid, true);
+ (void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG);
x = strchr(line, ':');
if (!x) {
@@ -218,7 +217,7 @@ int change_uid_gid(const char *user, char **_home) {
truncate_nl(line);
- wait_for_terminate_and_warn("getent initgroups", pid, true);
+ (void) wait_for_terminate_and_check("getent initgroups", pid, WAIT_LOG);
/* Skip over the username and subsequent separator whitespace */
x = line;
diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c
index 7f2f8f1f13..58f4636866 100644
--- a/src/nspawn/nspawn-stub-pid1.c
+++ b/src/nspawn/nspawn-stub-pid1.c
@@ -25,6 +25,7 @@
#include "fd-util.h"
#include "log.h"
+#include "missing.h"
#include "nspawn-stub-pid1.h"
#include "process-util.h"
#include "signal-util.h"
@@ -92,7 +93,7 @@ int stub_pid1(sd_id128_t uuid) {
sd_id128_to_string(uuid, new_environment + sizeof(new_environment) - SD_ID128_STRING_MAX);
reset_environ(new_environment, sizeof(new_environment));
- rename_process("STUBINIT");
+ (void) rename_process("(sd-stubinit)");
assert_se(sigemptyset(&waitmask) >= 0);
assert_se(sigset_add_many(&waitmask,
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 71b14e2302..0f05ecff03 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -1313,13 +1313,14 @@ static int userns_lchown(const char *p, uid_t uid, gid_t gid) {
static int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid_t gid) {
const char *q;
+ int r;
q = prefix_roota(root, path);
- if (mkdir(q, mode) < 0) {
- if (errno == EEXIST)
- return 0;
- return -errno;
- }
+ r = mkdir_errno_wrapper(q, mode);
+ if (r == -EEXIST)
+ return 0;
+ if (r < 0)
+ return r;
return userns_lchown(q, uid, gid);
}
@@ -1599,8 +1600,10 @@ static int setup_pts(const char *dest) {
/* Mount /dev/pts itself */
p = prefix_roota(dest, "/dev/pts");
- if (mkdir(p, 0755) < 0)
- return log_error_errno(errno, "Failed to create /dev/pts: %m");
+ r = mkdir_errno_wrapper(p, 0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create /dev/pts: %m");
+
r = mount_verbose(LOG_ERR, "devpts", p, "devpts", MS_NOSUID|MS_NOEXEC, options);
if (r < 0)
return r;
@@ -1846,12 +1849,13 @@ static int setup_journal(const char *directory) {
/* don't create parents here — if the host doesn't have
* permanent journal set up, don't force it here */
- if (mkdir(p, 0755) < 0 && errno != EEXIST) {
+ r = mkdir_errno_wrapper(p, 0755);
+ if (r < 0 && r != -EEXIST) {
if (try) {
- log_debug_errno(errno, "Failed to create %s, skipping journal setup: %m", p);
+ log_debug_errno(r, "Failed to create %s, skipping journal setup: %m", p);
return 0;
} else
- return log_error_errno(errno, "Failed to create %s: %m", p);
+ return log_error_errno(r, "Failed to create %s: %m", p);
}
} else if (access(p, F_OK) < 0)
@@ -2159,8 +2163,11 @@ static int determine_names(void) {
if (!arg_ephemeral)
arg_read_only = arg_read_only || i->read_only;
- } else
- arg_directory = get_current_dir_name();
+ } else {
+ r = safe_getcwd(&arg_directory);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine current directory: %m");
+ }
if (!arg_directory && !arg_image) {
log_error("Failed to determine path, please use -D or -i.");
@@ -3512,9 +3519,11 @@ static int run(int master,
}
/* Wait for the outer child. */
- r = wait_for_terminate_and_warn("namespace helper", *pid, NULL);
- if (r != 0)
- return r < 0 ? r : -EIO;
+ r = wait_for_terminate_and_check("(sd-namespace)", *pid, WAIT_LOG_ABNORMAL);
+ if (r < 0)
+ return r;
+ if (r != EXIT_SUCCESS)
+ return -EIO;
/* And now retrieve the PID of the inner child. */
l = recv(pid_socket_pair[0], pid, sizeof *pid, 0);
@@ -3616,14 +3625,16 @@ static int run(int master,
* case PID 1 will send us a friendly RequestStop signal, when it is asked to terminate the
* scope. Let's hook into that, and cleanly shut down the container, and print a friendly message. */
- r = sd_bus_add_match(bus, NULL,
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Scope',"
- "member='RequestStop'",
- on_request_stop, PID_TO_PTR(*pid));
+ r = sd_bus_match_signal_async(
+ bus,
+ NULL,
+ "org.freedesktop.systemd1",
+ NULL,
+ "org.freedesktop.systemd1.Scope",
+ "RequestStop",
+ on_request_stop, NULL, PID_TO_PTR(*pid));
if (r < 0)
- return log_error_errno(r, "Failed to install request stop match: %m");
+ return log_error_errno(r, "Failed to request RequestStop match: %m");
}
if (arg_register) {
diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c
index cab3c22bb2..fe6ce4cbbf 100644
--- a/src/nss-resolve/nss-resolve.c
+++ b/src/nss-resolve/nss-resolve.c
@@ -30,6 +30,7 @@
#include "in-addr-util.h"
#include "macro.h"
#include "nss-util.h"
+#include "resolved-def.h"
#include "string-util.h"
#include "util.h"
#include "signal-util.h"
@@ -37,8 +38,6 @@
NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
-#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
-
static bool bus_error_shall_fallback(sd_bus_error *e) {
return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) ||
@@ -157,7 +156,7 @@ enum nss_status _nss_resolve_gethostbyname4_r(
if (r < 0)
goto fail;
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
*errnop = ESRCH;
@@ -335,7 +334,7 @@ enum nss_status _nss_resolve_gethostbyname3_r(
if (r < 0)
goto fail;
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
*errnop = ESRCH;
@@ -533,7 +532,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
if (r < 0)
goto fail;
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
*errnop = ESRCH;
diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c
index cc641e1615..f75405d2e5 100644
--- a/src/nss-systemd/nss-systemd.c
+++ b/src/nss-systemd/nss-systemd.c
@@ -136,7 +136,8 @@ enum nss_status _nss_systemd_getpwnam_r(
*errnop = 0;
return NSS_STATUS_SUCCESS;
}
- if (streq(name, nobody_passwd.pw_name)) {
+ if (synthesize_nobody() &&
+ streq(name, nobody_passwd.pw_name)) {
*pwd = nobody_passwd;
*errnop = 0;
return NSS_STATUS_SUCCESS;
@@ -244,7 +245,8 @@ enum nss_status _nss_systemd_getpwuid_r(
*errnop = 0;
return NSS_STATUS_SUCCESS;
}
- if (uid == nobody_passwd.pw_uid) {
+ if (synthesize_nobody() &&
+ uid == nobody_passwd.pw_uid) {
*pwd = nobody_passwd;
*errnop = 0;
return NSS_STATUS_SUCCESS;
@@ -351,7 +353,8 @@ enum nss_status _nss_systemd_getgrnam_r(
*errnop = 0;
return NSS_STATUS_SUCCESS;
}
- if (streq(name, nobody_group.gr_name)) {
+ if (synthesize_nobody() &&
+ streq(name, nobody_group.gr_name)) {
*gr = nobody_group;
*errnop = 0;
return NSS_STATUS_SUCCESS;
@@ -456,7 +459,8 @@ enum nss_status _nss_systemd_getgrgid_r(
*errnop = 0;
return NSS_STATUS_SUCCESS;
}
- if (gid == nobody_group.gr_gid) {
+ if (synthesize_nobody() &&
+ gid == nobody_group.gr_gid) {
*gr = nobody_group;
*errnop = 0;
return NSS_STATUS_SUCCESS;
diff --git a/src/partition/growfs.c b/src/partition/growfs.c
index 901b33e39e..41b4e872be 100644
--- a/src/partition/growfs.c
+++ b/src/partition/growfs.c
@@ -28,6 +28,7 @@
#include <sys/types.h>
#include <sys/vfs.h>
+#include "blockdev-util.h"
#include "crypt-util.h"
#include "device-nodes.h"
#include "dissect-image.h"
diff --git a/src/partition/makefs.c b/src/partition/makefs.c
index e5e125255b..a957967dfe 100644
--- a/src/partition/makefs.c
+++ b/src/partition/makefs.c
@@ -28,12 +28,14 @@
#include "alloc-util.h"
#include "dissect-image.h"
+#include "process-util.h"
#include "signal-util.h"
#include "string-util.h"
static int makefs(const char *type, const char *device) {
const char *mkfs;
pid_t pid;
+ int r;
if (streq(type, "swap"))
mkfs = "/sbin/mkswap";
@@ -42,24 +44,19 @@ static int makefs(const char *type, const char *device) {
if (access(mkfs, X_OK) != 0)
return log_error_errno(errno, "%s is not executable: %m", mkfs);
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "fork(): %m");
-
- if (pid == 0) {
+ r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
const char *cmdline[3] = { mkfs, device, NULL };
/* Child */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
execv(cmdline[0], (char**) cmdline);
_exit(EXIT_FAILURE);
}
- return wait_for_terminate_and_warn(mkfs, pid, true);
+ return wait_for_terminate_and_check(mkfs, pid, WAIT_LOG);
}
int main(int argc, char *argv[]) {
diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c
index ec5be21a34..1bf718e4f6 100644
--- a/src/quotacheck/quotacheck.c
+++ b/src/quotacheck/quotacheck.c
@@ -71,14 +71,6 @@ static void test_files(void) {
}
int main(int argc, char *argv[]) {
-
- static const char * const cmdline[] = {
- QUOTACHECK,
- "-anug",
- NULL
- };
-
- pid_t pid;
int r;
if (argc > 1) {
@@ -106,25 +98,22 @@ int main(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
- pid = fork();
- if (pid < 0) {
- r = log_error_errno(errno, "fork(): %m");
+ r = safe_fork("(quotacheck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ if (r < 0)
goto finish;
- }
- if (pid == 0) {
+ if (r == 0) {
+ static const char * const cmdline[] = {
+ QUOTACHECK,
+ "-anug",
+ NULL
+ };
/* Child */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
execv(cmdline[0], (char**) cmdline);
- _exit(1); /* Operational error */
+ _exit(EXIT_FAILURE); /* Operational error */
}
- r = wait_for_terminate_and_warn("quotacheck", pid, true);
-
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c
index 196947ca51..2762fa7e89 100644
--- a/src/rc-local-generator/rc-local-generator.c
+++ b/src/rc-local-generator/rc-local-generator.c
@@ -23,7 +23,6 @@
#include <stdio.h>
#include <unistd.h>
-#include "alloc-util.h"
#include "log.h"
#include "mkdir.h"
#include "string-util.h"
@@ -32,21 +31,16 @@
static const char *arg_dest = "/tmp";
static int add_symlink(const char *service, const char *where) {
- _cleanup_free_ char *from = NULL, *to = NULL;
+ const char *from, *to;
int r;
assert(service);
assert(where);
- from = strjoin(SYSTEM_DATA_UNIT_PATH, "/", service);
- if (!from)
- return log_oom();
+ from = strjoina(SYSTEM_DATA_UNIT_PATH "/", service);
+ to = strjoina(arg_dest, "/", where, ".wants/", service);
- to = strjoin(arg_dest, "/", where, ".wants/", service);
- if (!to)
- return log_oom();
-
- mkdir_parents_label(to, 0755);
+ (void) mkdir_parents_label(to, 0755);
r = symlink(from, to);
if (r < 0) {
@@ -60,7 +54,7 @@ static int add_symlink(const char *service, const char *where) {
}
int main(int argc, char *argv[]) {
- int r = EXIT_SUCCESS;
+ int ret = EXIT_SUCCESS;
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
@@ -70,7 +64,8 @@ int main(int argc, char *argv[]) {
if (argc > 1)
arg_dest = argv[1];
- log_set_target(LOG_TARGET_SAFE);
+ log_set_prohibit_ipc(true);
+ log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
@@ -80,15 +75,15 @@ int main(int argc, char *argv[]) {
log_debug("Automatically adding rc-local.service.");
if (add_symlink("rc-local.service", "multi-user.target") < 0)
- r = EXIT_FAILURE;
+ ret = EXIT_FAILURE;
}
if (access(RC_LOCAL_SCRIPT_PATH_STOP, X_OK) >= 0) {
log_debug("Automatically adding halt-local.service.");
if (add_symlink("halt-local.service", "final.target") < 0)
- r = EXIT_FAILURE;
+ ret = EXIT_FAILURE;
}
- return r;
+ return ret;
}
diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c
index 2d7cf723f4..c61777c3fe 100644
--- a/src/remount-fs/remount-fs.c
+++ b/src/remount-fs/remount-fs.c
@@ -87,19 +87,12 @@ int main(int argc, char *argv[]) {
log_debug("Remounting %s", me->mnt_dir);
- pid = fork();
- if (pid < 0) {
- r = log_error_errno(errno, "Failed to fork: %m");
+ r = safe_fork("(remount)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
goto finish;
- }
-
- if (pid == 0) {
+ if (r == 0) {
/* Child */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- (void) prctl(PR_SET_PDEATHSIG, SIGTERM);
-
execv(MOUNT_PATH, STRV_MAKE(MOUNT_PATH, me->mnt_dir, "-o", "remount"));
log_error_errno(errno, "Failed to execute " MOUNT_PATH ": %m");
diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c
index 347252a90f..0c366b5e8e 100644
--- a/src/resolve/dns-type.c
+++ b/src/resolve/dns-type.c
@@ -19,6 +19,7 @@
***/
#include <sys/socket.h>
+#include <errno.h>
#include "dns-type.h"
#include "parse-util.h"
diff --git a/src/resolve/meson.build b/src/resolve/meson.build
index ee1acb5166..15752d24ff 100644
--- a/src/resolve/meson.build
+++ b/src/resolve/meson.build
@@ -31,7 +31,7 @@ basic_dns_sources = files('''
dns_type_h = files('dns-type.h')[0]
-systemd_resolved_only_sources = files('''
+systemd_resolved_sources = files('''
resolved.c
resolved-manager.c
resolved-manager.h
@@ -80,7 +80,7 @@ systemd_resolved_only_sources = files('''
resolved-etc-hosts.c
'''.split())
-systemd_resolve_only_sources = files('resolve-tool.c')
+systemd_resolve_sources = files('resolve-tool.c')
############################################################
@@ -141,14 +141,13 @@ resolved_dnssd_gperf_c = custom_target(
output : 'resolved-dnssd-gperf.c',
command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@'])
-systemd_resolved_sources = (basic_dns_sources +
- [resolved_gperf_c, resolved_dnssd_gperf_c] +
- systemd_resolved_only_sources +
- dns_type_headers)
+libsystemd_resolve_core = static_library(
+ 'systemd-resolve-core',
+ basic_dns_sources,
+ dns_type_headers,
+ include_directories : includes)
-systemd_resolve_sources = (basic_dns_sources +
- systemd_resolve_only_sources +
- dns_type_headers)
+systemd_resolved_sources += [resolved_gperf_c, resolved_dnssd_gperf_c]
if conf.get('ENABLE_RESOLVE') == 1
install_data('org.freedesktop.resolve1.conf',
@@ -178,37 +177,37 @@ endif
tests += [
[['src/resolve/test-resolve-tables.c',
- basic_dns_sources,
dns_type_headers,
'src/shared/test-tables.h'],
- [],
+ [libsystemd_resolve_core,
+ libshared],
[libgcrypt,
libgpg_error,
libm],
'ENABLE_RESOLVE'],
[['src/resolve/test-dns-packet.c',
- basic_dns_sources,
dns_type_headers],
- [],
+ [libsystemd_resolve_core,
+ libshared],
[libgcrypt,
libgpg_error,
libm],
'ENABLE_RESOLVE'],
[['src/resolve/test-resolved-packet.c',
- basic_dns_sources,
dns_type_headers],
- [],
+ [libsystemd_resolve_core,
+ libshared],
[libgcrypt,
libgpg_error,
libm],
'ENABLE_RESOLVE'],
[['src/resolve/test-dnssec.c',
- basic_dns_sources,
dns_type_headers],
- [],
+ [libsystemd_resolve_core,
+ libshared],
[libgcrypt,
libgpg_error,
libm],
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index 0252bdfcd7..2a6bf94070 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -41,8 +41,6 @@
#include "strv.h"
#include "terminal-util.h"
-#define DNS_CALL_TIMEOUT_USEC (90*USEC_PER_SEC)
-
static int arg_family = AF_UNSPEC;
static int arg_ifindex = 0;
static uint16_t arg_type = 0;
@@ -175,7 +173,7 @@ static int resolve_host(sd_bus *bus, const char *name) {
ts = now(CLOCK_MONOTONIC);
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0)
return log_error_errno(r, "%s: resolve call failed: %s", name, bus_error_message(&error, r));
@@ -306,7 +304,7 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
ts = now(CLOCK_MONOTONIC);
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
return r;
@@ -442,7 +440,7 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
ts = now(CLOCK_MONOTONIC);
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
if (warn_missing || r != -ENXIO)
log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
@@ -685,7 +683,7 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
ts = now(CLOCK_MONOTONIC);
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0)
return log_error_errno(r, "Resolve call failed: %s", bus_error_message(&error, r));
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 9157d9ea68..ffd7c4824e 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -1844,18 +1844,6 @@ static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_VTABLE_END,
};
-static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
- Manager *m = userdata;
-
- assert(s);
- assert(m);
-
- m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
-
- manager_connect_bus(m);
- return 0;
-}
-
static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
Manager *m = userdata;
int b, r;
@@ -1886,20 +1874,9 @@ int manager_connect_bus(Manager *m) {
if (m->bus)
return 0;
- r = sd_bus_default_system(&m->bus);
- if (r < 0) {
- /* We failed to connect? Yuck, we must be in early
- * boot. Let's try in 5s again. */
-
- log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m");
-
- r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m);
- if (r < 0)
- return log_error_errno(r, "Failed to install bus reconnect time event: %m");
-
- (void) sd_event_source_set_description(m->bus_retry_event_source, "bus-retry");
- return 0;
- }
+ r = bus_open_system_watch_bind(&m->bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to system bus: %m");
r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
if (r < 0)
@@ -1921,24 +1898,26 @@ int manager_connect_bus(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to register dnssd enumerator: %m");
- r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
+ r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.resolve1", 0, NULL, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to register name: %m");
+ return log_error_errno(r, "Failed to request name: %m");
r = sd_bus_attach_event(m->bus, m->event, 0);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
- r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot,
- "type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Manager',"
- "member='PrepareForSleep',"
- "path='/org/freedesktop/login1'",
- match_prepare_for_sleep,
- m);
- if (r < 0)
- log_error_errno(r, "Failed to add match for PrepareForSleep: %m");
+ r = sd_bus_match_signal_async(
+ m->bus,
+ &m->prepare_for_sleep_slot,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "PrepareForSleep",
+ match_prepare_for_sleep,
+ NULL,
+ m);
+ if (r < 0)
+ log_warning_errno(r, "Failed to request match for PrepareForSleep, ignoring: %m");
return 0;
}
diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h
index 64c2b1503e..96f93107ad 100644
--- a/src/resolve/resolved-def.h
+++ b/src/resolve/resolved-def.h
@@ -22,6 +22,8 @@
#include <inttypes.h>
+#include "time-util.h"
+
#define SD_RESOLVED_DNS (UINT64_C(1) << 0)
#define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) << 1)
#define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) << 2)
@@ -37,3 +39,5 @@
#define SD_RESOLVED_MDNS (SD_RESOLVED_MDNS_IPV4|SD_RESOLVED_MDNS_IPV6)
#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_MDNS|SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)
+
+#define SD_RESOLVED_QUERY_TIMEOUT_USEC (120 * USEC_PER_SEC)
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index 942956dd71..e9197f1dfd 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -476,7 +476,7 @@ static int dns_cache_put_positive(
if (r < 0)
return r;
- if (log_get_max_level() >= LOG_DEBUG) {
+ if (DEBUG_LOGGING) {
_cleanup_free_ char *t = NULL;
(void) in_addr_to_string(i->owner_family, &i->owner_address, &t);
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index d7a839a823..2067dd5182 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -18,6 +18,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#if HAVE_GCRYPT
+#include <gcrypt.h>
+#endif
+
#include "alloc-util.h"
#include "dns-domain.h"
#include "resolved-dns-packet.h"
@@ -752,13 +756,20 @@ int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, in
static const uint8_t rfc6975[] = {
0, 5, /* OPTION_CODE: DAU */
+#if HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600
+ 0, 7, /* LIST_LENGTH */
+#else
0, 6, /* LIST_LENGTH */
+#endif
DNSSEC_ALGORITHM_RSASHA1,
DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
DNSSEC_ALGORITHM_RSASHA256,
DNSSEC_ALGORITHM_RSASHA512,
DNSSEC_ALGORITHM_ECDSAP256SHA256,
DNSSEC_ALGORITHM_ECDSAP384SHA384,
+#if HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600
+ DNSSEC_ALGORITHM_ED25519,
+#endif
0, 6, /* OPTION_CODE: DHU */
0, 3, /* LIST_LENGTH */
@@ -1837,6 +1848,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
if (r < 0)
return r;
+ if (rdlength < 4)
+ return -EBADMSG;
+
r = dns_packet_read_memdup(p, rdlength - 4,
&rr->ds.digest, &rr->ds.digest_size,
NULL);
@@ -1859,6 +1873,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
if (r < 0)
return r;
+ if (rdlength < 2)
+ return -EBADMSG;
+
r = dns_packet_read_memdup(p, rdlength - 2,
&rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size,
NULL);
@@ -1883,6 +1900,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
if (r < 0)
return r;
+ if (rdlength < 4)
+ return -EBADMSG;
+
r = dns_packet_read_memdup(p, rdlength - 4,
&rr->dnskey.key, &rr->dnskey.key_size,
NULL);
@@ -1927,6 +1947,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
if (r < 0)
return r;
+ if (rdlength + offset < p->rindex)
+ return -EBADMSG;
+
r = dns_packet_read_memdup(p, offset + rdlength - p->rindex,
&rr->rrsig.signature, &rr->rrsig.signature_size,
NULL);
@@ -2016,6 +2039,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
if (r < 0)
return r;
+ if (rdlength < 3)
+ return -EBADMSG;
+
r = dns_packet_read_memdup(p, rdlength - 3,
&rr->tlsa.data, &rr->tlsa.data_size,
NULL);
@@ -2036,6 +2062,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
if (r < 0)
return r;
+ if (rdlength + offset < p->rindex)
+ return -EBADMSG;
+
r = dns_packet_read_memdup(p,
rdlength + offset - p->rindex,
&rr->caa.value, &rr->caa.value_size, NULL);
@@ -2111,19 +2140,11 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
return true;
}
-int dns_packet_extract(DnsPacket *p) {
+static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question) {
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {};
unsigned n, i;
int r;
- if (p->extracted)
- return 0;
-
- INIT_REWINDER(rewinder, p);
- dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
-
n = DNS_PACKET_QDCOUNT(p);
if (n > 0) {
question = dns_question_new(n);
@@ -2150,107 +2171,146 @@ int dns_packet_extract(DnsPacket *p) {
}
}
+ *ret_question = question;
+ question = NULL;
+ return 0;
+}
+
+static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ unsigned n, i;
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *previous = NULL;
+ bool bad_opt = false;
+ int r;
+
n = DNS_PACKET_RRCOUNT(p);
- if (n > 0) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *previous = NULL;
- bool bad_opt = false;
+ if (n == 0)
+ return 0;
- answer = dns_answer_new(n);
- if (!answer)
- return -ENOMEM;
+ answer = dns_answer_new(n);
+ if (!answer)
+ return -ENOMEM;
- for (i = 0; i < n; i++) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- bool cache_flush = false;
+ for (i = 0; i < n; i++) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ bool cache_flush = false;
- r = dns_packet_read_rr(p, &rr, &cache_flush, NULL);
- if (r < 0)
- return r;
+ r = dns_packet_read_rr(p, &rr, &cache_flush, NULL);
+ if (r < 0)
+ return r;
- /* Try to reduce memory usage a bit */
- if (previous)
- dns_resource_key_reduce(&rr->key, &previous->key);
+ /* Try to reduce memory usage a bit */
+ if (previous)
+ dns_resource_key_reduce(&rr->key, &previous->key);
- if (rr->key->type == DNS_TYPE_OPT) {
- bool has_rfc6975;
+ if (rr->key->type == DNS_TYPE_OPT) {
+ bool has_rfc6975;
- if (p->opt || bad_opt) {
- /* Multiple OPT RRs? if so, let's ignore all, because there's something wrong
- * with the server, and if one is valid we wouldn't know which one. */
- log_debug("Multiple OPT RRs detected, ignoring all.");
- bad_opt = true;
- continue;
- }
+ if (p->opt || bad_opt) {
+ /* Multiple OPT RRs? if so, let's ignore all, because there's
+ * something wrong with the server, and if one is valid we wouldn't
+ * know which one. */
+ log_debug("Multiple OPT RRs detected, ignoring all.");
+ bad_opt = true;
+ continue;
+ }
- if (!dns_name_is_root(dns_resource_key_name(rr->key))) {
- /* If the OPT RR is not owned by the root domain, then it is bad, let's ignore
- * it. */
- log_debug("OPT RR is not owned by root domain, ignoring.");
- bad_opt = true;
- continue;
- }
+ if (!dns_name_is_root(dns_resource_key_name(rr->key))) {
+ /* If the OPT RR is not owned by the root domain, then it is bad,
+ * let's ignore it. */
+ log_debug("OPT RR is not owned by root domain, ignoring.");
+ bad_opt = true;
+ continue;
+ }
- if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) {
- /* OPT RR is in the wrong section? Some Belkin routers do this. This is a hint
- * the EDNS implementation is borked, like the Belkin one is, hence ignore
- * it. */
- log_debug("OPT RR in wrong section, ignoring.");
- bad_opt = true;
- continue;
+ if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) {
+ /* OPT RR is in the wrong section? Some Belkin routers do this. This
+ * is a hint the EDNS implementation is borked, like the Belkin one
+ * is, hence ignore it. */
+ log_debug("OPT RR in wrong section, ignoring.");
+ bad_opt = true;
+ continue;
+ }
+
+ if (!opt_is_good(rr, &has_rfc6975)) {
+ log_debug("Malformed OPT RR, ignoring.");
+ bad_opt = true;
+ continue;
+ }
+
+ if (DNS_PACKET_QR(p)) {
+ /* Additional checks for responses */
+
+ if (!DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(rr)) {
+ /* If this is a reply and we don't know the EDNS version
+ * then something is weird... */
+ log_debug("EDNS version newer that our request, bad server.");
+ return -EBADMSG;
}
- if (!opt_is_good(rr, &has_rfc6975)) {
- log_debug("Malformed OPT RR, ignoring.");
+ if (has_rfc6975) {
+ /* If the OPT RR contains RFC6975 algorithm data, then this
+ * is indication that the server just copied the OPT it got
+ * from us (which contained that data) back into the reply.
+ * If so, then it doesn't properly support EDNS, as RFC6975
+ * makes it very clear that the algorithm data should only
+ * be contained in questions, never in replies. Crappy
+ * Belkin routers copy the OPT data for example, hence let's
+ * detect this so that we downgrade early. */
+ log_debug("OPT RR contained RFC6975 data, ignoring.");
bad_opt = true;
continue;
}
+ }
- if (DNS_PACKET_QR(p)) {
- /* Additional checks for responses */
-
- if (!DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(rr)) {
- /* If this is a reply and we don't know the EDNS version then something
- * is weird... */
- log_debug("EDNS version newer that our request, bad server.");
- return -EBADMSG;
- }
-
- if (has_rfc6975) {
- /* If the OPT RR contains RFC6975 algorithm data, then this is indication that
- * the server just copied the OPT it got from us (which contained that data)
- * back into the reply. If so, then it doesn't properly support EDNS, as
- * RFC6975 makes it very clear that the algorithm data should only be contained
- * in questions, never in replies. Crappy Belkin routers copy the OPT data for
- * example, hence let's detect this so that we downgrade early. */
- log_debug("OPT RR contained RFC6975 data, ignoring.");
- bad_opt = true;
- continue;
- }
- }
+ p->opt = dns_resource_record_ref(rr);
+ } else {
+ /* According to RFC 4795, section 2.9. only the RRs from the Answer section
+ * shall be cached. Hence mark only those RRs as cacheable by default, but
+ * not the ones from the Additional or Authority sections. */
+ DnsAnswerFlags flags =
+ (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
+ (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0);
+
+ r = dns_answer_add(answer, rr, p->ifindex, flags);
+ if (r < 0)
+ return r;
+ }
- p->opt = dns_resource_record_ref(rr);
- } else {
+ /* Remember this RR, so that we potentically can merge it's ->key object with the
+ * next RR. Note that we only do this if we actually decided to keep the RR around.
+ */
+ dns_resource_record_unref(previous);
+ previous = dns_resource_record_ref(rr);
+ }
- /* According to RFC 4795, section 2.9. only the RRs from the Answer section shall be
- * cached. Hence mark only those RRs as cacheable by default, but not the ones from the
- * Additional or Authority sections. */
+ if (bad_opt)
+ p->opt = dns_resource_record_unref(p->opt);
- r = dns_answer_add(answer, rr, p->ifindex,
- (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
- (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0));
- if (r < 0)
- return r;
- }
+ *ret_answer = answer;
+ answer = NULL;
+ return 0;
+}
- /* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note
- * that we only do this if we actually decided to keep the RR around. */
- dns_resource_record_unref(previous);
- previous = dns_resource_record_ref(rr);
- }
+int dns_packet_extract(DnsPacket *p) {
+ _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {};
+ int r;
- if (bad_opt)
- p->opt = dns_resource_record_unref(p->opt);
- }
+ if (p->extracted)
+ return 0;
+
+ INIT_REWINDER(rewinder, p);
+ dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
+
+ r = dns_packet_extract_question(p, &question);
+ if (r < 0)
+ return r;
+
+ r = dns_packet_extract_answer(p, &answer);
+ if (r < 0)
+ return r;
p->question = question;
question = NULL;
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index 227d0b5d11..5f51340743 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -28,9 +28,6 @@
#include "resolved-etc-hosts.h"
#include "string-util.h"
-/* How long to wait for the query in total */
-#define QUERY_TIMEOUT_USEC (60 * USEC_PER_SEC)
-
#define CNAME_MAX 8
#define QUERIES_MAX 2048
#define AUXILIARY_QUERIES_MAX 64
@@ -769,8 +766,8 @@ int dns_query_go(DnsQuery *q) {
q->manager->event,
&q->timeout_event_source,
clock_boottime_or_monotonic(),
- now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0,
- on_query_timeout, q);
+ now(clock_boottime_or_monotonic()) + SD_RESOLVED_QUERY_TIMEOUT_USEC,
+ 0, on_query_timeout, q);
if (r < 0)
goto fail;
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index eb5592d3cf..4056bda558 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -517,9 +517,13 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
case DNS_TYPE_OPENPGPKEY:
default:
- free(rr->generic.data);
+ if (!rr->unparseable)
+ free(rr->generic.data);
}
+ if (rr->unparseable)
+ free(rr->generic.data);
+
free(rr->wire_format);
dns_resource_key_unref(rr->key);
}
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index ea4459a89a..bf6aac8300 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -885,13 +885,17 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata
scope->conflict_event_source = sd_event_source_unref(scope->conflict_event_source);
for (;;) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- rr = ordered_hashmap_steal_first(scope->conflict_queue);
- if (!rr)
+ key = ordered_hashmap_first_key(scope->conflict_queue);
+ if (!key)
break;
+ rr = ordered_hashmap_remove(scope->conflict_queue, key);
+ assert(rr);
+
r = dns_scope_make_conflict_packet(scope, rr, &p);
if (r < 0) {
log_error_errno(r, "Failed to make conflict packet: %m");
@@ -930,6 +934,7 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) {
if (r < 0)
return log_debug_errno(r, "Failed to queue conflicting RR: %m");
+ dns_resource_key_ref(rr->key);
dns_resource_record_ref(rr);
if (scope->conflict_event_source)
@@ -953,7 +958,7 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) {
}
void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) {
- unsigned i;
+ DnsResourceRecord *rr;
int r;
assert(scope);
@@ -984,21 +989,24 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) {
log_debug("Checking for conflicts...");
- for (i = 0; i < p->answer->n_rrs; i++) {
+ DNS_ANSWER_FOREACH(rr, p->answer) {
+ /* No conflict if it is DNS-SD RR used for service enumeration. */
+ if (dns_resource_key_is_dnssd_ptr(rr->key))
+ continue;
/* Check for conflicts against the local zone. If we
* found one, we won't check any further */
- r = dns_zone_check_conflicts(&scope->zone, p->answer->items[i].rr);
+ r = dns_zone_check_conflicts(&scope->zone, rr);
if (r != 0)
continue;
/* Check for conflicts against the local cache. If so,
* send out an advisory query, to inform everybody */
- r = dns_cache_check_conflicts(&scope->cache, p->answer->items[i].rr, p->family, &p->sender);
+ r = dns_cache_check_conflicts(&scope->cache, rr, p->family, &p->sender);
if (r <= 0)
continue;
- dns_scope_notify_conflict(scope, p->answer->items[i].rr);
+ dns_scope_notify_conflict(scope, rr);
}
}
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index d470a64524..68c5d5c1e3 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -30,7 +30,7 @@
/* After how much time to repeat classic DNS requests */
#define DNS_TIMEOUT_MIN_USEC (750 * USEC_PER_MSEC)
-#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
+#define DNS_TIMEOUT_MAX_USEC (SD_RESOLVED_QUERY_TIMEOUT_USEC / DNS_TRANSACTION_ATTEMPTS_MAX)
/* The amount of time to wait before retrying with a full feature set */
#define DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC (6 * USEC_PER_HOUR)
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 2dbf432df9..2ee027791a 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -488,7 +488,8 @@ static int manager_watch_hostname(Manager *m) {
assert(m);
- m->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY);
+ m->hostname_fd = open("/proc/sys/kernel/hostname",
+ O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (m->hostname_fd < 0) {
log_warning_errno(errno, "Failed to watch hostname: %m");
return 0;
@@ -1410,7 +1411,7 @@ void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResource
assert(verdict >= 0);
assert(verdict < _DNSSEC_VERDICT_MAX);
- if (log_get_max_level() >= LOG_DEBUG) {
+ if (DEBUG_LOGGING) {
char s[DNS_RESOURCE_KEY_STRING_MAX];
log_debug("Found verdict for lookup %s: %s",
diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c
index 458c908fad..c1f118fecc 100644
--- a/src/resolve/test-dns-packet.c
+++ b/src/resolve/test-dns-packet.c
@@ -21,6 +21,8 @@
#include <net/if.h>
#include <glob.h>
+#include "sd-id128.h"
+
#include "alloc-util.h"
#include "fileio.h"
#include "glob-util.h"
diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c
index e7b077939f..514f72eeeb 100644
--- a/src/resolve/test-dnssec-complex.c
+++ b/src/resolve/test-dnssec-complex.c
@@ -27,11 +27,10 @@
#include "bus-common-errors.h"
#include "dns-type.h"
#include "random-util.h"
+#include "resolved-def.h"
#include "string-util.h"
#include "time-util.h"
-#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
-
static void prefix_random(const char *name, char **ret) {
uint64_t i, u;
char *m = NULL;
@@ -75,7 +74,7 @@ static void test_rr_lookup(sd_bus *bus, const char *name, uint16_t type, const c
assert_se(sd_bus_message_append(req, "isqqt", 0, name, DNS_CLASS_IN, type, UINT64_C(0)) >= 0);
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
assert_se(result);
@@ -112,7 +111,7 @@ static void test_hostname_lookup(sd_bus *bus, const char *name, int family, cons
assert_se(sd_bus_message_append(req, "isit", 0, name, family, UINT64_C(0)) >= 0);
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
assert_se(result);
diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c
index 2d2b5e31bf..ebabfba96d 100644
--- a/src/resolve/test-dnssec.c
+++ b/src/resolve/test-dnssec.c
@@ -19,7 +19,9 @@
***/
#include <arpa/inet.h>
+#if HAVE_GCRYPT
#include <gcrypt.h>
+#endif
#include <netinet/in.h>
#include <sys/socket.h>
diff --git a/src/run/run.c b/src/run/run.c
index 5d7441ac93..a30501169c 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -40,6 +40,7 @@
#include "spawn-polkit-agent.h"
#include "strv.h"
#include "terminal-util.h"
+#include "unit-def.h"
#include "unit-name.h"
#include "user-util.h"
@@ -68,6 +69,8 @@ static enum {
ARG_STDIO_DIRECT, /* Directly pass our stdin/stdout/stderr to the activated service, useful for usage in shell pipelines, requested by --pipe */
ARG_STDIO_AUTO, /* If --pipe and --pty are used together we use --pty when invoked on a TTY, and --pipe otherwise */
} arg_stdio = ARG_STDIO_NONE;
+static char **arg_path_property = NULL;
+static char **arg_socket_property = NULL;
static char **arg_timer_property = NULL;
static bool with_timer = false;
static bool arg_quiet = false;
@@ -101,6 +104,10 @@ static void help(void) {
" -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n"
" -q --quiet Suppress information messages during runtime\n"
" -G --collect Unload unit after it ran, even when failed\n\n"
+ "Path options:\n"
+ " --path-property=NAME=VALUE Set path unit property\n\n"
+ "Socket options:\n"
+ " --socket-property=NAME=VALUE Set socket unit property\n\n"
"Timer options:\n"
" --on-active=SECONDS Run after SECONDS delay\n"
" --on-boot=SECONDS Run SECONDS after machine was booted up\n"
@@ -113,7 +120,7 @@ static void help(void) {
}
static int add_timer_property(const char *name, const char *val) {
- _cleanup_free_ char *p = NULL;
+ char *p;
assert(name);
assert(val);
@@ -125,8 +132,6 @@ static int add_timer_property(const char *name, const char *val) {
if (strv_consume(&arg_timer_property, p) < 0)
return log_oom();
- p = NULL;
-
return 0;
}
@@ -152,6 +157,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_ON_UNIT_INACTIVE,
ARG_ON_CALENDAR,
ARG_TIMER_PROPERTY,
+ ARG_PATH_PROPERTY,
+ ARG_SOCKET_PROPERTY,
ARG_NO_BLOCK,
ARG_NO_ASK_PASSWORD,
ARG_WAIT,
@@ -188,12 +195,15 @@ static int parse_argv(int argc, char *argv[]) {
{ "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE },
{ "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
{ "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
+ { "path-property", required_argument, NULL, ARG_PATH_PROPERTY },
+ { "socket-property", required_argument, NULL, ARG_SOCKET_PROPERTY },
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "collect", no_argument, NULL, 'G' },
{},
};
+ bool with_trigger = false;
int r, c;
assert(argc >= 0);
@@ -368,6 +378,20 @@ static int parse_argv(int argc, char *argv[]) {
!!startswith(optarg, "OnCalendar=");
break;
+ case ARG_PATH_PROPERTY:
+
+ if (strv_extend(&arg_path_property, optarg) < 0)
+ return log_oom();
+
+ break;
+
+ case ARG_SOCKET_PROPERTY:
+
+ if (strv_extend(&arg_socket_property, optarg) < 0)
+ return log_oom();
+
+ break;
+
case ARG_NO_BLOCK:
arg_no_block = true;
break;
@@ -387,6 +411,13 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
+ with_trigger = !!arg_path_property || !!arg_socket_property || with_timer;
+
+ /* currently, only single trigger (path, socket, timer) unit can be created simultaneously */
+ if ((int) !!arg_path_property + (int) !!arg_socket_property + (int) with_timer > 1) {
+ log_error("Only single trigger (path, socket, timer) unit can be created.");
+ return -EINVAL;
+ }
if (arg_stdio == ARG_STDIO_AUTO) {
/* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully
@@ -397,7 +428,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_STDIO_DIRECT;
}
- if ((optind >= argc) && (!arg_unit || !with_timer)) {
+ if ((optind >= argc) && (!arg_unit || !with_trigger)) {
log_error("Command line to execute required.");
return -EINVAL;
}
@@ -417,7 +448,7 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- if (arg_stdio != ARG_STDIO_NONE && (with_timer || arg_scope)) {
+ if (arg_stdio != ARG_STDIO_NONE && (with_trigger || arg_scope)) {
log_error("--pty/--pipe is not compatible in timer or --scope mode.");
return -EINVAL;
}
@@ -432,8 +463,8 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- if (arg_scope && with_timer) {
- log_error("Timer options are not supported in --scope mode.");
+ if (arg_scope && with_trigger) {
+ log_error("Path, socket or timer options are not supported in --scope mode.");
return -EINVAL;
}
@@ -448,8 +479,8 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- if (with_timer) {
- log_error("--wait may not be combined with timer operations.");
+ if (with_trigger) {
+ log_error("--wait may not be combined with path, socket or timer operations.");
return -EINVAL;
}
@@ -462,7 +493,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
+static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) {
int r;
r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
@@ -475,7 +506,7 @@ static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
return bus_log_create_error(r);
}
- r = bus_append_unit_property_assignment_many(m, properties);
+ r = bus_append_unit_property_assignment_many(m, t, properties);
if (r < 0)
return r;
@@ -521,7 +552,7 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
assert(m);
- r = transient_unit_set_properties(m, arg_property);
+ r = transient_unit_set_properties(m, UNIT_SERVICE, arg_property);
if (r < 0)
return r;
@@ -694,7 +725,7 @@ static int transient_scope_set_properties(sd_bus_message *m) {
assert(m);
- r = transient_unit_set_properties(m, arg_property);
+ r = transient_unit_set_properties(m, UNIT_SCOPE, arg_property);
if (r < 0)
return r;
@@ -718,7 +749,7 @@ static int transient_timer_set_properties(sd_bus_message *m) {
assert(m);
- r = transient_unit_set_properties(m, arg_timer_property);
+ r = transient_unit_set_properties(m, UNIT_TIMER, arg_timer_property);
if (r < 0)
return r;
@@ -1028,7 +1059,6 @@ static int start_transient_service(
.inactive_enter_usec = USEC_INFINITY,
};
_cleanup_free_ char *path = NULL;
- const char *mt;
c.bus = sd_bus_ref(bus);
@@ -1058,18 +1088,20 @@ static int start_transient_service(
if (!path)
return log_oom();
- mt = strjoina("type='signal',"
- "sender='org.freedesktop.systemd1',"
- "path='", path, "',"
- "interface='org.freedesktop.DBus.Properties',"
- "member='PropertiesChanged'");
- r = sd_bus_add_match(bus, &c.match, mt, on_properties_changed, &c);
+ r = sd_bus_match_signal_async(
+ bus,
+ &c.match,
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ on_properties_changed, NULL, &c);
if (r < 0)
- return log_error_errno(r, "Failed to add properties changed signal.");
+ return log_error_errno(r, "Failed to request properties changed signal match: %m");
r = sd_bus_attach_event(bus, c.event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
- return log_error_errno(r, "Failed to attach bus to event loop.");
+ return log_error_errno(r, "Failed to attach bus to event loop: %m");
r = run_context_update(&c, path);
if (r < 0)
@@ -1282,14 +1314,15 @@ static int start_transient_scope(
return log_error_errno(errno, "Failed to execute: %m");
}
-static int start_transient_timer(
+static int start_transient_trigger(
sd_bus *bus,
- char **argv) {
+ char **argv,
+ const char *suffix) {
_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;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
- _cleanup_free_ char *timer = NULL, *service = NULL;
+ _cleanup_free_ char *trigger = NULL, *service = NULL;
const char *object = NULL;
int r;
@@ -1308,17 +1341,17 @@ static int start_transient_timer(
if (!service)
return log_oom();
- r = unit_name_change_suffix(service, ".timer", &timer);
+ r = unit_name_change_suffix(service, suffix, &trigger);
if (r < 0)
return log_error_errno(r, "Failed to change unit suffix: %m");
break;
case UNIT_TIMER:
- timer = strdup(arg_unit);
- if (!timer)
+ trigger = strdup(arg_unit);
+ if (!trigger)
return log_oom();
- r = unit_name_change_suffix(timer, ".service", &service);
+ r = unit_name_change_suffix(trigger, ".service", &service);
if (r < 0)
return log_error_errno(r, "Failed to change unit suffix: %m");
break;
@@ -1328,7 +1361,7 @@ static int start_transient_timer(
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
- r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".timer", &timer);
+ r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, suffix, &trigger);
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
@@ -1339,7 +1372,7 @@ static int start_transient_timer(
if (r < 0)
return r;
- r = unit_name_change_suffix(service, ".timer", &timer);
+ r = unit_name_change_suffix(service, suffix, &trigger);
if (r < 0)
return log_error_errno(r, "Failed to change unit suffix: %m");
}
@@ -1359,7 +1392,7 @@ static int start_transient_timer(
return bus_log_create_error(r);
/* Name and Mode */
- r = sd_bus_message_append(m, "ss", timer, "fail");
+ r = sd_bus_message_append(m, "ss", trigger, "fail");
if (r < 0)
return bus_log_create_error(r);
@@ -1368,7 +1401,14 @@ static int start_transient_timer(
if (r < 0)
return bus_log_create_error(r);
- r = transient_timer_set_properties(m);
+ if (streq(suffix, ".path"))
+ r = transient_unit_set_properties(m, UNIT_PATH, arg_path_property);
+ else if (streq(suffix, ".socket"))
+ r = transient_unit_set_properties(m, UNIT_SOCKET, arg_socket_property);
+ else if (streq(suffix, ".timer"))
+ r = transient_timer_set_properties(m);
+ else
+ assert_not_reached("Invalid suffix");
if (r < 0)
return r;
@@ -1414,7 +1454,7 @@ static int start_transient_timer(
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
- log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r));
+ log_error("Failed to start transient %s unit: %s", suffix + 1, bus_error_message(&error, -r));
return r;
}
@@ -1427,7 +1467,7 @@ static int start_transient_timer(
return r;
if (!arg_quiet) {
- log_info("Running timer as unit: %s", timer);
+ log_info("Running %s as unit: %s", suffix + 1, trigger);
if (argv[0])
log_info("Will run service as unit: %s", service);
}
@@ -1488,14 +1528,20 @@ int main(int argc, char* argv[]) {
if (arg_scope)
r = start_transient_scope(bus, argv + optind);
+ else if (arg_path_property)
+ r = start_transient_trigger(bus, argv + optind, ".path");
+ else if (arg_socket_property)
+ r = start_transient_trigger(bus, argv + optind, ".socket");
else if (with_timer)
- r = start_transient_timer(bus, argv + optind);
+ r = start_transient_trigger(bus, argv + optind, ".timer");
else
r = start_transient_service(bus, argv + optind, &retval);
finish:
strv_free(arg_environment);
strv_free(arg_property);
+ strv_free(arg_path_property);
+ strv_free(arg_socket_property);
strv_free(arg_timer_property);
return r < 0 ? EXIT_FAILURE : retval;
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index 17928a9732..99d6a9b143 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -316,7 +316,7 @@ int ask_password_tty(
}
if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
- flush_fd(notify);
+ (void) flush_fd(notify);
if (pollfd[POLL_TTY].revents == 0)
continue;
@@ -656,7 +656,7 @@ int ask_password_agent(
goto finish;
}
- if (strv_length(l) <= 0) {
+ if (strv_isempty(l)) {
l = strv_free(l);
log_debug("Invalid packet");
continue;
diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c
index c0a10417d8..9c3bdd47de 100644
--- a/src/shared/bootspec.c
+++ b/src/shared/bootspec.c
@@ -20,6 +20,8 @@
#include <stdio.h>
#include <linux/magic.h>
+#include "sd-id128.h"
+
#include "alloc-util.h"
#include "blkid-util.h"
#include "bootspec.h"
@@ -52,22 +54,30 @@ void boot_entry_free(BootEntry *entry) {
}
int boot_entry_load(const char *path, BootEntry *entry) {
+ _cleanup_(boot_entry_free) BootEntry tmp = {};
_cleanup_fclose_ FILE *f = NULL;
unsigned line = 1;
- _cleanup_(boot_entry_free) BootEntry tmp = {};
+ char *b, *c;
int r;
assert(path);
assert(entry);
- f = fopen(path, "re");
- if (!f)
- return log_error_errno(errno, "Failed to open \"%s\": %m", path);
+ c = endswith_no_case(path, ".conf");
+ if (!c) {
+ log_error("Invalid loader entry filename: %s", path);
+ return -EINVAL;
+ }
- tmp.filename = strdup(basename(path));
+ b = basename(path);
+ tmp.filename = strndup(b, c - b);
if (!tmp.filename)
return log_oom();
+ f = fopen(path, "re");
+ if (!f)
+ return log_error_errno(errno, "Failed to open \"%s\": %m", path);
+
for (;;) {
_cleanup_free_ char *buf = NULL;
char *p;
@@ -195,67 +205,8 @@ int boot_loader_read_conf(const char *path, BootConfig *config) {
return 0;
}
-/* This is a direct translation of str_verscmp from boot.c */
-static bool is_digit(int c) {
- return c >= '0' && c <= '9';
-}
-
-static int c_order(int c) {
- if (c == '\0')
- return 0;
- if (is_digit(c))
- return 0;
- else if ((c >= 'a') && (c <= 'z'))
- return c;
- else
- return c + 0x10000;
-}
-
-static int str_verscmp(const char *s1, const char *s2) {
- const char *os1 = s1;
- const char *os2 = s2;
-
- while (*s1 || *s2) {
- int first;
-
- while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
- int order;
-
- order = c_order(*s1) - c_order(*s2);
- if (order)
- return order;
- s1++;
- s2++;
- }
-
- while (*s1 == '0')
- s1++;
- while (*s2 == '0')
- s2++;
-
- first = 0;
- while (is_digit(*s1) && is_digit(*s2)) {
- if (first == 0)
- first = *s1 - *s2;
- s1++;
- s2++;
- }
-
- if (is_digit(*s1))
- return 1;
- if (is_digit(*s2))
- return -1;
-
- if (first != 0)
- return first;
- }
-
- return strcmp(os1, os2);
-}
-
static int boot_entry_compare(const void *a, const void *b) {
- const BootEntry *aa = a;
- const BootEntry *bb = b;
+ const BootEntry *aa = a, *bb = b;
return str_verscmp(aa->filename, bb->filename);
}
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index b58abed2b5..bc77c3abdb 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -24,6 +24,7 @@
#include "bus-util.h"
#include "cap-list.h"
#include "cgroup-util.h"
+#include "condition.h"
#include "cpu-set-util.h"
#include "env-util.h"
#include "errno-list.h"
@@ -42,9 +43,11 @@
#include "rlimit-util.h"
#include "securebits-util.h"
#include "signal-util.h"
+#include "socket-protocol-list.h"
#include "string-util.h"
#include "syslog-util.h"
#include "terminal-util.h"
+#include "unit-def.h"
#include "user-util.h"
#include "utf8.h"
#include "util.h"
@@ -70,478 +73,456 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
&u->job_path);
}
-static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
+#define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \
+ static int bus_append_##parse_func( \
+ sd_bus_message *m, \
+ const char *field, \
+ const char *eq) { \
+ type val; \
+ int r; \
+ \
+ r = parse_func(eq, &val); \
+ if (r < 0) \
+ return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \
+ \
+ r = sd_bus_message_append(m, "(sv)", field, \
+ bus_type, (cast_type) val); \
+ if (r < 0) \
+ return bus_log_create_error(r); \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func) \
+ static int bus_append_##parse_func( \
+ sd_bus_message *m, \
+ const char *field, \
+ const char *eq) { \
+ int r; \
+ \
+ r = parse_func(eq); \
+ if (r < 0) { \
+ log_error("Failed to parse %s: %s", field, eq); \
+ return -EINVAL; \
+ } \
+ \
+ r = sd_bus_message_append(m, "(sv)", field, \
+ bus_type, (int32_t) r); \
+ if (r < 0) \
+ return bus_log_create_error(r); \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+DEFINE_BUS_APPEND_PARSE("b", parse_boolean);
+DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string);
+DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string);
+DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string);
+DEFINE_BUS_APPEND_PARSE("i", log_level_from_string);
+DEFINE_BUS_APPEND_PARSE("i", parse_errno);
+DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string);
+DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string);
+DEFINE_BUS_APPEND_PARSE("i", signal_from_string_try_harder);
+DEFINE_BUS_APPEND_PARSE("i", socket_protocol_from_name);
+DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority);
+DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice);
+DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi);
+DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t, parse_nsec);
+DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse);
+DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse);
+DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse);
+DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string);
+DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64);
+DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode);
+DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou);
+DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64);
+
+static inline int bus_append_string(sd_bus_message *m, const char *field, const char *eq) {
int r;
- assert(m);
- assert(prefix);
-
- r = sd_bus_message_open_container(m, 'r', "iayu");
+ r = sd_bus_message_append(m, "(sv)", field, "s", eq);
if (r < 0)
- return r;
+ return bus_log_create_error(r);
- r = sd_bus_message_append(m, "i", family);
+ return 1;
+}
+
+static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, ExtractFlags flags) {
+ const char *p;
+ int r;
+
+ r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
- return r;
+ return bus_log_create_error(r);
- r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family));
+ r = sd_bus_message_append_basic(m, 's', field);
if (r < 0)
- return r;
+ return bus_log_create_error(r);
- r = sd_bus_message_append(m, "u", prefixlen);
+ r = sd_bus_message_open_container(m, 'v', "as");
if (r < 0)
- return r;
+ return bus_log_create_error(r);
- return sd_bus_message_close_container(m);
-}
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if (r < 0)
+ return bus_log_create_error(r);
-int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
- const char *eq, *field;
- UnitDependency dep;
- int r, rl;
+ for (p = eq;;) {
+ _cleanup_free_ char *word = NULL;
- assert(m);
- assert(assignment);
+ r = extract_first_word(&p, &word, NULL, flags);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_error_errno(r, "Invalid syntax: %s", eq);
- eq = strchr(assignment, '=');
- if (!eq) {
- log_error("Not an assignment: %s", assignment);
- return -EINVAL;
+ r = sd_bus_message_append_basic(m, 's', word);
+ if (r < 0)
+ return bus_log_create_error(r);
}
- r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
- field = strndupa(assignment, eq - assignment);
- eq++;
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- if (streq(field, "CPUQuota")) {
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- if (isempty(eq))
- r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
- else {
- r = parse_percent_unbounded(eq);
- if (r <= 0) {
- log_error_errno(r, "CPU quota '%s' invalid.", eq);
- return -EINVAL;
- }
+ return 1;
+}
- r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U);
- }
+static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) {
+ int r;
- goto finish;
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
- } else if (streq(field, "EnvironmentFile")) {
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ if (r < 0)
+ return bus_log_create_error(r);
- if (isempty(eq))
- r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 0);
- else
- r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1,
- eq[0] == '-' ? eq + 1 : eq,
- eq[0] == '-');
- goto finish;
+ r = sd_bus_message_open_container(m, 'v', "ay");
+ if (r < 0)
+ return bus_log_create_error(r);
- } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
- char *n;
- usec_t t;
- size_t l;
+ r = sd_bus_message_append_array(m, 'y', buf, n);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = parse_sec(eq, &t);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- l = strlen(field);
- n = newa(char, l + 2);
- if (!n)
- return log_oom();
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- /* Change suffix Sec → USec */
- strcpy(mempcpy(n, field, l - 3), "USec");
- r = sd_bus_message_append(m, "sv", n, "t", t);
- goto finish;
+ return 1;
+}
- } else if (streq(field, "LogExtraFields")) {
+static int bus_append_parse_sec_rename(sd_bus_message *m, const char *field, const char *eq) {
+ char *n;
+ usec_t t;
+ size_t l;
+ int r;
- r = sd_bus_message_append(m, "s", "LogExtraFields");
- if (r < 0)
- goto finish;
+ r = parse_sec(eq, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
- r = sd_bus_message_open_container(m, 'v', "aay");
- if (r < 0)
- goto finish;
+ l = strlen(field);
+ n = newa(char, l + 2);
+ /* Change suffix Sec → USec */
+ strcpy(mempcpy(n, field, l - 3), "USec");
- r = sd_bus_message_open_container(m, 'a', "ay");
- if (r < 0)
- goto finish;
+ r = sd_bus_message_append(m, "(sv)", n, "t", t);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
- if (r < 0)
- goto finish;
+ return 1;
+}
- r = sd_bus_message_close_container(m);
- if (r < 0)
- goto finish;
+static int bus_append_parse_size(sd_bus_message *m, const char *field, const char *eq, uint64_t base) {
+ uint64_t v;
+ int r;
- r = sd_bus_message_close_container(m);
- goto finish;
+ r = parse_size(eq, base, &v);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
- } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit")) {
- uint64_t bytes;
+ r = sd_bus_message_append(m, "(sv)", field, "t", v);
+ if (r < 0)
+ return bus_log_create_error(r);
- if (isempty(eq) || streq(eq, "infinity"))
- bytes = CGROUP_LIMIT_MAX;
- else {
- r = parse_percent(eq);
- if (r >= 0) {
- char *n;
+ return 1;
+}
- /* When this is a percentage we'll convert this into a relative value in the range
- * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
- * ones). This way the physical memory size can be determined server-side */
+static int bus_append_exec_command(sd_bus_message *m, const char *field, const char *eq) {
+ bool ignore_failure = false, explicit_path = false, done = false;
+ _cleanup_strv_free_ char **l = NULL;
+ _cleanup_free_ char *path = NULL;
+ int r;
- n = strjoina(field, "Scale");
- r = sd_bus_message_append(m, "sv", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
- goto finish;
+ do {
+ switch (*eq) {
- } else {
- r = parse_size(eq, 1024, &bytes);
- if (r < 0)
- return log_error_errno(r, "Failed to parse bytes specification %s", assignment);
+ case '-':
+ if (ignore_failure)
+ done = true;
+ else {
+ ignore_failure = true;
+ eq++;
}
- }
-
- r = sd_bus_message_append(m, "sv", field, "t", bytes);
- goto finish;
-
- } else if (streq(field, "Delegate")) {
-
- r = parse_boolean(eq);
- if (r < 0) {
- const char *p = eq;
-
- r = sd_bus_message_append(m, "s", "DelegateControllers");
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_open_container(m, 'v', "as");
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_open_container(m, 'a', "s");
- if (r < 0)
- goto finish;
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
-
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
- if (r == 0)
- break;
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- return log_error_errno(r, "Invalid syntax: %s", eq);
+ break;
- r = sd_bus_message_append(m, "s", word);
- if (r < 0)
- goto finish;
+ case '@':
+ if (explicit_path)
+ done = true;
+ else {
+ explicit_path = true;
+ eq++;
}
+ break;
- r = sd_bus_message_close_container(m);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_close_container(m);
- } else
- r = sd_bus_message_append(m, "sv", "Delegate", "b", r);
-
- goto finish;
-
- } else if (streq(field, "TasksMax")) {
- uint64_t t;
-
- if (isempty(eq) || streq(eq, "infinity"))
- t = (uint64_t) -1;
- else {
- r = parse_percent(eq);
- if (r >= 0) {
- r = sd_bus_message_append(m, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
- goto finish;
- } else {
- r = safe_atou64(eq, &t);
- if (r < 0)
- return log_error_errno(r, "Failed to parse maximum tasks specification %s", assignment);
- }
+ case '+':
+ case '!':
+ /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
+ log_error("Sorry, but +, ! and !! are currently not supported for transient services.");
+ return -EOPNOTSUPP;
+ default:
+ done = true;
+ break;
}
+ } while (!done);
- r = sd_bus_message_append(m, "sv", "TasksMax", "t", t);
- goto finish;
-
- } else if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) {
- const char *n, *appended;
+ if (explicit_path) {
+ r = extract_first_word(&eq, &path, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse path: %m");
+ }
- n = startswith(eq, "fd:");
- if (n) {
- appended = strjoina(field, "FileDescriptorName");
- r = sd_bus_message_append(m, "sv", appended, "s", n);
+ r = strv_split_extract(&l, eq, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse command line: %m");
- } else if ((n = startswith(eq, "file:"))) {
- appended = strjoina(field, "File");
- r = sd_bus_message_append(m, "sv", appended, "s", n);
- } else
- r = sd_bus_message_append(m, "sv", field, "s", eq);
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
- goto finish;
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ if (r < 0)
+ return bus_log_create_error(r);
- } else if (streq(field, "StandardInputText")) {
- _cleanup_free_ char *unescaped = NULL;
+ r = sd_bus_message_open_container(m, 'v', "a(sasb)");
+ if (r < 0)
+ return bus_log_create_error(r);
- r = cunescape(eq, 0, &unescaped);
- if (r < 0)
- return log_error_errno(r, "Failed to unescape text '%s': %m", eq);
+ r = sd_bus_message_open_container(m, 'a', "(sasb)");
+ if (r < 0)
+ return bus_log_create_error(r);
- if (!strextend(&unescaped, "\n", NULL))
- return log_oom();
+ if (!strv_isempty(l)) {
- /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
- * interface anyway */
+ r = sd_bus_message_open_container(m, 'r', "sasb");
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_append(m, "s", "StandardInputData");
+ r = sd_bus_message_append(m, "s", path ?: l[0]);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_open_container(m, 'v', "ay");
+ r = sd_bus_message_append_strv(m, l);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append_array(m, 'y', unescaped, strlen(unescaped));
+ r = sd_bus_message_append(m, "b", ignore_failure);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_close_container(m);
- goto finish;
+ if (r < 0)
+ return bus_log_create_error(r);
}
- r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
- rl = rlimit_from_string(field);
- if (rl >= 0) {
- const char *sn;
- struct rlimit l;
-
- r = rlimit_parse(rl, eq, &l);
- if (r < 0)
- return log_error_errno(r, "Failed to parse resource limit: %s", eq);
-
- r = sd_bus_message_append(m, "v", "t", l.rlim_max);
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
- if (r < 0)
- return bus_log_create_error(r);
+ return 1;
+}
- sn = strjoina(field, "Soft");
- r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur);
-
- } else if (STR_IN_SET(field,
- "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
- "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem",
- "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
- "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
- "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse", "Persistent",
- "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
- "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
- "CPUSchedulingResetOnFork", "LockPersonality")) {
+static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
+ int r;
- r = parse_boolean(eq);
- if (r < 0)
- return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment);
+ assert(m);
+ assert(prefix);
- r = sd_bus_message_append(m, "v", "b", r);
+ r = sd_bus_message_open_container(m, 'r', "iayu");
+ if (r < 0)
+ return r;
- } else if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight")) {
- uint64_t u;
+ r = sd_bus_message_append(m, "i", family);
+ if (r < 0)
+ return r;
- r = cg_weight_parse(eq, &u);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
+ r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family));
+ if (r < 0)
+ return r;
- r = sd_bus_message_append(m, "v", "t", u);
+ r = sd_bus_message_append(m, "u", prefixlen);
+ if (r < 0)
+ return r;
- } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) {
- uint64_t u;
+ return sd_bus_message_close_container(m);
+}
- r = cg_cpu_shares_parse(eq, &u);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
+static int bus_append_cgroup_property(sd_bus_message *m, const char *field, const char *eq) {
+ int r;
- r = sd_bus_message_append(m, "v", "t", u);
+ if (STR_IN_SET(field, "DevicePolicy", "Slice"))
- } else if (STR_IN_SET(field, "IOWeight", "StartupIOWeight")) {
- uint64_t u;
+ return bus_append_string(m, field, eq);
- r = cg_weight_parse(eq, &u);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
+ if (STR_IN_SET(field,
+ "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
+ "TasksAccounting", "IPAccounting"))
- r = sd_bus_message_append(m, "v", "t", u);
+ return bus_append_parse_boolean(m, field, eq);
- } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) {
- uint64_t u;
+ if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight"))
- r = cg_blkio_weight_parse(eq, &u);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
+ return bus_append_cg_weight_parse(m, field, eq);
- r = sd_bus_message_append(m, "v", "t", u);
+ if (STR_IN_SET(field, "CPUShares", "StartupCPUShares"))
- } else if (STR_IN_SET(field,
- "User", "Group", "DevicePolicy", "KillMode",
- "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
- "Description", "Slice", "Type", "WorkingDirectory",
- "RootDirectory", "SyslogIdentifier", "ProtectSystem",
- "ProtectHome", "SELinuxContext", "Restart", "RootImage",
- "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
- "KeyringMode", "CollectMode", "FailureAction", "SuccessAction",
- "OnCalendar"))
+ return bus_append_cg_cpu_shares_parse(m, field, eq);
- r = sd_bus_message_append(m, "v", "s", eq);
+ if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight"))
- else if (streq(field, "StandardInputData")) {
- _cleanup_free_ void *decoded = NULL;
- size_t sz;
+ return bus_append_cg_blkio_weight_parse(m, field, eq);
- r = unbase64mem(eq, (size_t) -1, &decoded, &sz);
- if (r < 0)
- return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
+ if (streq(field, "Delegate")) {
- r = sd_bus_message_open_container(m, 'v', "ay");
+ r = parse_boolean(eq);
if (r < 0)
- return bus_log_create_error(r);
+ return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_QUOTES);
- r = sd_bus_message_append_array(m, 'y', decoded, sz);
+ r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_close_container(m);
+ return 1;
+ }
- } else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {
- bool ignore;
- const char *s;
+ if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) {
- if (eq[0] == '-') {
- ignore = true;
- s = eq + 1;
- } else {
- ignore = false;
- s = eq;
+ if (isempty(eq) || streq(eq, "infinity")) {
+ r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX);
+ if (r < 0)
+ return bus_log_create_error(r);
+ return 1;
}
- r = sd_bus_message_append(m, "v", "(bs)", ignore, s);
-
- } else if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax")) {
- int level;
+ r = parse_percent(eq);
+ if (r >= 0) {
+ char *n;
- level = log_level_from_string(eq);
- if (level < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
+ /* When this is a percentage we'll convert this into a relative value in the range
+ * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
+ * ones). This way the physical memory size can be determined server-side */
- r = sd_bus_message_append(m, "v", "i", level);
-
- } else if (streq(field, "SyslogFacility")) {
- int facility;
+ n = strjoina(field, "Scale");
+ r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
+ if (r < 0)
+ return bus_log_create_error(r);
- facility = log_facility_unshifted_from_string(eq);
- if (facility < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
+ return 1;
}
- r = sd_bus_message_append(m, "v", "i", facility);
+ if (streq(field, "TasksMax"))
+ return bus_append_safe_atou64(m, field, eq);
- } else if (streq(field, "SecureBits")) {
+ return bus_append_parse_size(m, field, eq, 1024);
- r = secure_bits_from_string(eq);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
+ }
- r = sd_bus_message_append(m, "v", "i", r);
+ if (streq(field, "CPUQuota")) {
- } else if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) {
- uint64_t sum = 0;
- bool invert = false;
- const char *p;
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
+ else {
+ r = parse_percent_unbounded(eq);
+ if (r <= 0) {
+ log_error_errno(r, "CPU quota '%s' invalid.", eq);
+ return -EINVAL;
+ }
- p = eq;
- if (*p == '~') {
- invert = true;
- p++;
+ r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U);
}
- r = capability_set_from_string(p, &sum);
if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
-
- sum = invert ? ~sum : sum;
+ return bus_log_create_error(r);
- r = sd_bus_message_append(m, "v", "t", sum);
+ return 1;
+ }
- } else if (streq(field, "DeviceAllow")) {
+ if (streq(field, "DeviceAllow")) {
if (isempty(eq))
- r = sd_bus_message_append(m, "v", "a(ss)", 0);
+ r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
else {
- const char *path, *rwm, *e;
+ const char *path = eq, *rwm = NULL, *e;
e = strchr(eq, ' ');
if (e) {
path = strndupa(eq, e - eq);
rwm = e+1;
- } else {
- path = eq;
- rwm = "";
- }
-
- if (!is_deviceallow_pattern(path)) {
- log_error("%s is not a device file in /dev.", path);
- return -EINVAL;
}
- r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
+ r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, strempty(rwm));
}
- } else if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
+ if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
if (isempty(eq))
- r = sd_bus_message_append(m, "v", "a(st)", 0);
+ r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
else {
const char *path, *bandwidth, *e;
uint64_t bytes;
e = strchr(eq, ' ');
- if (e) {
- path = strndupa(eq, e - eq);
- bandwidth = e+1;
- } else {
+ if (!e) {
log_error("Failed to parse %s value %s.", field, eq);
return -EINVAL;
}
- if (!path_startswith(path, "/dev")) {
- log_error("%s is not a device file in /dev.", path);
- return -EINVAL;
- }
+ path = strndupa(eq, e - eq);
+ bandwidth = e+1;
if (streq(bandwidth, "infinity")) {
bytes = CGROUP_LIMIT_MAX;
@@ -551,315 +532,290 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
}
- r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
+ r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes);
}
- } else if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) {
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
+ if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) {
if (isempty(eq))
- r = sd_bus_message_append(m, "v", "a(st)", 0);
+ r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
else {
const char *path, *weight, *e;
uint64_t u;
e = strchr(eq, ' ');
- if (e) {
- path = strndupa(eq, e - eq);
- weight = e+1;
- } else {
+ if (!e) {
log_error("Failed to parse %s value %s.", field, eq);
return -EINVAL;
}
- if (!path_startswith(path, "/dev")) {
- log_error("%s is not a device file in /dev.", path);
- return -EINVAL;
- }
+ path = strndupa(eq, e - eq);
+ weight = e+1;
r = safe_atou64(weight, &u);
if (r < 0)
return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight);
- r = sd_bus_message_append(m, "v", "a(st)", 1, path, u);
+ r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u);
}
- } else if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) {
+ if (r < 0)
+ return bus_log_create_error(r);
- if (isempty(eq))
- r = sd_bus_message_append(m, "v", "a(iayu)", 0);
- else {
- unsigned char prefixlen;
- union in_addr_union prefix = {};
- int family;
+ return 1;
+ }
- r = sd_bus_message_open_container(m, 'v', "a(iayu)");
- if (r < 0)
- return bus_log_create_error(r);
+ if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) {
+ unsigned char prefixlen;
+ union in_addr_union prefix = {};
+ int family;
- r = sd_bus_message_open_container(m, 'a', "(iayu)");
+ if (isempty(eq)) {
+ r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0);
if (r < 0)
return bus_log_create_error(r);
- if (streq(eq, "any")) {
- /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
+ return 1;
+ }
- r = bus_append_ip_address_access(m, AF_INET, &prefix, 0);
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
- r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0);
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ if (r < 0)
+ return bus_log_create_error(r);
- } else if (is_localhost(eq)) {
- /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
+ r = sd_bus_message_open_container(m, 'v', "a(iayu)");
+ if (r < 0)
+ return bus_log_create_error(r);
- prefix.in.s_addr = htobe32(0x7f000000);
- r = bus_append_ip_address_access(m, AF_INET, &prefix, 8);
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_open_container(m, 'a', "(iayu)");
+ if (r < 0)
+ return bus_log_create_error(r);
- prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
- r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128);
- if (r < 0)
- return r;
+ if (streq(eq, "any")) {
+ /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
- } else if (streq(eq, "link-local")) {
+ r = bus_append_ip_address_access(m, AF_INET, &prefix, 0);
+ if (r < 0)
+ return bus_log_create_error(r);
- /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
+ r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0);
+ if (r < 0)
+ return bus_log_create_error(r);
- prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
- r = bus_append_ip_address_access(m, AF_INET, &prefix, 16);
- if (r < 0)
- return bus_log_create_error(r);
+ } else if (is_localhost(eq)) {
+ /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
- prefix.in6 = (struct in6_addr) {
- .s6_addr32[0] = htobe32(0xfe800000)
- };
- r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64);
- if (r < 0)
- return bus_log_create_error(r);
+ prefix.in.s_addr = htobe32(0x7f000000);
+ r = bus_append_ip_address_access(m, AF_INET, &prefix, 8);
+ if (r < 0)
+ return bus_log_create_error(r);
- } else if (streq(eq, "multicast")) {
+ prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
+ r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128);
+ if (r < 0)
+ return r;
- /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
+ } else if (streq(eq, "link-local")) {
+ /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
- prefix.in.s_addr = htobe32((UINT32_C(224) << 24));
- r = bus_append_ip_address_access(m, AF_INET, &prefix, 4);
- if (r < 0)
- return bus_log_create_error(r);
+ prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
+ r = bus_append_ip_address_access(m, AF_INET, &prefix, 16);
+ if (r < 0)
+ return bus_log_create_error(r);
- prefix.in6 = (struct in6_addr) {
- .s6_addr32[0] = htobe32(0xff000000)
- };
- r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8);
- if (r < 0)
- return bus_log_create_error(r);
+ prefix.in6 = (struct in6_addr) {
+ .s6_addr32[0] = htobe32(0xfe800000)
+ };
+ r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64);
+ if (r < 0)
+ return bus_log_create_error(r);
- } else {
- r = in_addr_prefix_from_string_auto(eq, &family, &prefix, &prefixlen);
- if (r < 0)
- return log_error_errno(r, "Failed to parse IP address prefix: %s", eq);
+ } else if (streq(eq, "multicast")) {
+ /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
- r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
- if (r < 0)
- return bus_log_create_error(r);
- }
+ prefix.in.s_addr = htobe32((UINT32_C(224) << 24));
+ r = bus_append_ip_address_access(m, AF_INET, &prefix, 4);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_close_container(m);
+ prefix.in6 = (struct in6_addr) {
+ .s6_addr32[0] = htobe32(0xff000000)
+ };
+ r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_close_container(m);
+ } else {
+ r = in_addr_prefix_from_string_auto(eq, &family, &prefix, &prefixlen);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse IP address prefix: %s", eq);
+
+ r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
if (r < 0)
return bus_log_create_error(r);
}
- } else if (streq(field, "CPUSchedulingPolicy")) {
- int n;
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- n = sched_policy_from_string(eq);
- if (n < 0)
- return log_error_errno(r, "Failed to parse CPUSchedulingPolicy: %s", eq);
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_append(m, "v", "i", (int32_t) n);
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- } else if (streq(field, "CPUSchedulingPriority")) {
- int n;
+ return 1;
+ }
- r = safe_atoi(eq, &n);
- if (r < 0)
- return log_error_errno(r, "Failed to parse CPUSchedulingPriority: %s", eq);
- if (!sched_priority_is_valid(n))
- return log_error_errno(r, "Invalid CPUSchedulingPriority: %s", eq);
+ return 0;
+}
- r = sd_bus_message_append(m, "v", "i", (int32_t) n);
+static int bus_append_automount_property(sd_bus_message *m, const char *field, const char *eq) {
- } else if (streq(field, "CPUAffinity")) {
- _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
- int ncpus;
+ if (streq(field, "Where"))
- ncpus = parse_cpu_set(eq, &cpuset);
- if (ncpus < 0)
- return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
+ return bus_append_string(m, field, eq);
- r = sd_bus_message_open_container(m, 'v', "ay");
- if (r < 0)
- return bus_log_create_error(r);
+ if (streq(field, "DirectoryMode"))
- r = sd_bus_message_append_array(m, 'y', cpuset, CPU_ALLOC_SIZE(ncpus));
- if (r < 0)
- return bus_log_create_error(r);
+ return bus_append_parse_mode(m, field, eq);
- r = sd_bus_message_close_container(m);
+ if (streq(field, "TimeoutIdleSec"))
- } else if (streq(field, "Nice")) {
- int n;
+ return bus_append_parse_sec_rename(m, field, eq);
- r = parse_nice(eq, &n);
- if (r < 0)
- return log_error_errno(r, "Failed to parse nice value: %s", eq);
+ return 0;
+}
- r = sd_bus_message_append(m, "v", "i", (int32_t) n);
+static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) {
+ int r, rl;
- } else if (streq(field, "SystemCallFilter")) {
- int whitelist;
- _cleanup_strv_free_ char **l = NULL;
- const char *p;
+ if (STR_IN_SET(field,
+ "User", "Group",
+ "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
+ "WorkingDirectory", "RootDirectory", "SyslogIdentifier",
+ "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage",
+ "RuntimeDirectoryPreserve", "Personality", "KeyringMode"))
- p = eq;
- if (*p == '~') {
- whitelist = 0;
- p++;
- } else
- whitelist = 1;
+ return bus_append_string(m, field, eq);
- if (whitelist != 0) {
- r = strv_extend(&l, "@default");
- if (r < 0)
- return log_oom();
- }
+ if (STR_IN_SET(field,
+ "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
+ "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
+ "NoNewPrivileges", "SyslogLevelPrefix",
+ "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
+ "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups",
+ "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality"))
- for (;;) {
- _cleanup_free_ char *word = NULL;
+ return bus_append_parse_boolean(m, field, eq);
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
- if (r == 0)
- break;
+ if (STR_IN_SET(field,
+ "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
+ "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths",
+ "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
+ "SupplementaryGroups", "SystemCallArchitectures"))
- r = strv_extend(&l, word);
- if (r < 0)
- return log_oom();
- }
+ return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
- r = sd_bus_message_open_container(m, 'v', "(bas)");
- if (r < 0)
- return bus_log_create_error(r);
+ if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax"))
- r = sd_bus_message_open_container(m, 'r', "bas");
- if (r < 0)
- return bus_log_create_error(r);
+ return bus_append_log_level_from_string(m, field, eq);
- r = sd_bus_message_append_basic(m, 'b', &whitelist);
- if (r < 0)
- return bus_log_create_error(r);
+ if (streq(field, "SyslogFacility"))
- r = sd_bus_message_append_strv(m, l);
- if (r < 0)
- return bus_log_create_error(r);
+ return bus_append_log_facility_unshifted_from_string(m, field, eq);
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
+ if (streq(field, "SecureBits"))
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
+ return bus_append_secure_bits_from_string(m, field, eq);
- } else if (streq(field, "SystemCallArchitectures")) {
- const char *p;
+ if (streq(field, "CPUSchedulingPolicy"))
- r = sd_bus_message_open_container(m, 'v', "as");
- if (r < 0)
- return bus_log_create_error(r);
+ return bus_append_sched_policy_from_string(m, field, eq);
- r = sd_bus_message_open_container(m, 'a', "s");
- if (r < 0)
- return bus_log_create_error(r);
+ if (STR_IN_SET(field, "CPUSchedulingPriority", "OOMScoreAdjust"))
- for (p = eq;;) {
- _cleanup_free_ char *word = NULL;
+ return bus_append_safe_atoi(m, field, eq);
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
- if (r == 0)
- break;
+ if (streq(field, "Nice"))
- r = sd_bus_message_append_basic(m, 's', word);
- if (r < 0)
- return bus_log_create_error(r);
- }
+ return bus_append_parse_nice(m, field, eq);
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
+ if (streq(field, "SystemCallErrorNumber"))
- r = sd_bus_message_close_container(m);
+ return bus_append_parse_errno(m, field, eq);
- } else if (streq(field, "SystemCallErrorNumber")) {
- int n;
+ if (streq(field, "IOSchedulingClass"))
- n = parse_errno(eq);
- if (n <= 0)
- return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
+ return bus_append_ioprio_class_from_string(m, field, eq);
- r = sd_bus_message_append(m, "v", "i", (int32_t) n);
+ if (streq(field, "IOSchedulingPriority"))
- } else if (streq(field, "RestrictAddressFamilies")) {
- int whitelist;
- _cleanup_strv_free_ char **l = NULL;
- const char *p = eq;
+ return bus_append_ioprio_parse_priority(m, field, eq);
- if (*p == '~') {
- whitelist = 0;
- p++;
- } else
- whitelist = 1;
+ if (STR_IN_SET(field,
+ "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
+ "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask"))
- for (;;) {
- _cleanup_free_ char *word = NULL;
+ return bus_append_parse_mode(m, field, eq);
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
- if (r == 0)
- break;
+ if (streq(field, "TimerSlackNSec"))
- r = strv_extend(&l, word);
- if (r < 0)
- return log_oom();
- }
+ return bus_append_parse_nsec(m, field, eq);
- r = sd_bus_message_open_container(m, 'v', "(bas)");
+ if (streq(field, "MountFlags"))
+
+ return bus_append_mount_propagation_flags_from_string(m, field, eq);
+
+ if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment"))
+
+ return bus_append_strv(m, field, eq, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
+
+ if (streq(field, "EnvironmentFile")) {
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
+ else
+ r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1,
+ eq[0] == '-' ? eq + 1 : eq,
+ eq[0] == '-');
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_open_container(m, 'r', "bas");
+ return 1;
+ }
+
+ if (streq(field, "LogExtraFields")) {
+
+ r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append_basic(m, 'b', &whitelist);
+ r = sd_bus_message_append_basic(m, 's', "LogExtraFields");
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append_strv(m, l);
+ r = sd_bus_message_open_container(m, 'v', "aay");
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_close_container(m);
+ r = sd_bus_message_open_container(m, 'a', "ay");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
if (r < 0)
return bus_log_create_error(r);
@@ -867,165 +823,158 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
if (r < 0)
return bus_log_create_error(r);
- } else if (streq(field, "FileDescriptorStoreMax")) {
- unsigned u;
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = safe_atou(eq, &u);
+ r = sd_bus_message_close_container(m);
if (r < 0)
- return log_error_errno(r, "Failed to parse file descriptor store limit: %s", eq);
+ return bus_log_create_error(r);
- r = sd_bus_message_append(m, "v", "u", (uint32_t) u);
+ return 1;
+ }
- } else if (streq(field, "IOSchedulingClass")) {
- int c;
+ if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) {
+ const char *n, *appended;
+
+ if ((n = startswith(eq, "fd:"))) {
+ appended = strjoina(field, "FileDescriptorName");
+ r = sd_bus_message_append(m, "(sv)", appended, "s", n);
+ } else if ((n = startswith(eq, "file:"))) {
+ appended = strjoina(field, "File");
+ r = sd_bus_message_append(m, "(sv)", appended, "s", n);
+ } else
+ r = sd_bus_message_append(m, "(sv)", field, "s", eq);
- c = ioprio_class_from_string(eq);
- if (c < 0)
- return log_error_errno(r, "Failed to parse IO scheduling class: %s", eq);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_append(m, "v", "i", (int32_t) c);
+ return 1;
+ }
- } else if (streq(field, "IOSchedulingPriority")) {
- int q;
+ if (streq(field, "StandardInputText")) {
+ _cleanup_free_ char *unescaped = NULL;
- r = ioprio_parse_priority(eq, &q);
+ r = cunescape(eq, 0, &unescaped);
if (r < 0)
- return log_error_errno(r, "Failed to parse IO scheduling priority: %s", eq);
+ return log_error_errno(r, "Failed to unescape text '%s': %m", eq);
- r = sd_bus_message_append(m, "v", "i", (int32_t) q);
+ if (!strextend(&unescaped, "\n", NULL))
+ return log_oom();
- } else if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) {
- const char *p;
+ /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
+ * interface anyway */
- r = sd_bus_message_open_container(m, 'v', "as");
- if (r < 0)
- return bus_log_create_error(r);
+ return bus_append_byte_array(m, field, unescaped, strlen(unescaped));
+ }
- r = sd_bus_message_open_container(m, 'a', "s");
+ if (streq(field, "StandardInputData")) {
+ _cleanup_free_ void *decoded = NULL;
+ size_t sz;
+
+ r = unbase64mem(eq, (size_t) -1, &decoded, &sz);
if (r < 0)
- return bus_log_create_error(r);
+ return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
- for (p = eq;;) {
- _cleanup_free_ char *word = NULL;
+ return bus_append_byte_array(m, field, decoded, sz);
+ }
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
- if (r < 0)
- return log_error_errno(r, "Failed to parse Environment value %s: %m", eq);
- if (r == 0)
- break;
+ rl = rlimit_from_string(field);
+ if (rl >= 0) {
+ const char *sn;
+ struct rlimit l;
- if (streq(field, "Environment")) {
- if (!env_assignment_is_valid(word)) {
- log_error("Invalid environment assignment: %s", word);
- return -EINVAL;
- }
- } else if (streq(field, "UnsetEnvironment")) {
- if (!env_assignment_is_valid(word) && !env_name_is_valid(word)) {
- log_error("Invalid environment name or assignment: %s", word);
- return -EINVAL;
- }
- } else { /* PassEnvironment */
- if (!env_name_is_valid(word)) {
- log_error("Invalid environment variable name: %s", word);
- return -EINVAL;
- }
- }
+ r = rlimit_parse(rl, eq, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse resource limit: %s", eq);
- r = sd_bus_message_append_basic(m, 's', word);
- if (r < 0)
- return bus_log_create_error(r);
- }
+ r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_close_container(m);
+ sn = strjoina(field, "Soft");
+ r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_close_container(m);
+ return 1;
+ }
- } else if (streq(field, "KillSignal")) {
- int sig;
+ if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {
+ int ignore = 0;
+ const char *s = eq;
- sig = signal_from_string_try_harder(eq);
- if (sig < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
+ if (eq[0] == '-') {
+ ignore = 1;
+ s = eq + 1;
}
- r = sd_bus_message_append(m, "v", "i", sig);
-
- } else if (streq(field, "TimerSlackNSec")) {
- nsec_t n;
-
- r = parse_nsec(eq, &n);
+ r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s);
if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
+ return bus_log_create_error(r);
- r = sd_bus_message_append(m, "v", "t", n);
- } else if (streq(field, "OOMScoreAdjust")) {
- int oa;
+ return 1;
+ }
- r = safe_atoi(eq, &oa);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
+ if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) {
+ uint64_t sum = 0;
+ bool invert = false;
+ const char *p = eq;
- if (!oom_score_adjust_is_valid(oa)) {
- log_error("OOM score adjust value out of range");
- return -EINVAL;
+ if (*p == '~') {
+ invert = true;
+ p++;
}
- r = sd_bus_message_append(m, "v", "i", oa);
- } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
- "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
- const char *p;
-
- r = sd_bus_message_open_container(m, 'v', "as");
+ r = capability_set_from_string(p, &sum);
if (r < 0)
- return bus_log_create_error(r);
+ return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
- r = sd_bus_message_open_container(m, 'a', "s");
+ sum = invert ? ~sum : sum;
+
+ r = sd_bus_message_append(m, "(sv)", field, "t", sum);
if (r < 0)
return bus_log_create_error(r);
- for (p = eq;;) {
- _cleanup_free_ char *word = NULL;
- size_t offset;
-
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
- if (r == 0)
- break;
+ return 1;
+ }
- if (!utf8_is_valid(word)) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
+ if (streq(field, "CPUAffinity")) {
+ _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
- offset = word[0] == '-';
- offset += word[offset] == '+';
+ r = parse_cpu_set(eq, &cpuset);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
- if (!path_is_absolute(word + offset)) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
+ return bus_append_byte_array(m, field, cpuset, CPU_ALLOC_SIZE(r));
+ }
- path_kill_slashes(word + offset);
+ if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) {
+ int whitelist = 1;
+ const char *p = eq;
- r = sd_bus_message_append_basic(m, 's', word);
- if (r < 0)
- return bus_log_create_error(r);
+ if (*p == '~') {
+ whitelist = 0;
+ p++;
}
- r = sd_bus_message_close_container(m);
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_close_container(m);
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ if (r < 0)
+ return bus_log_create_error(r);
- } else if (streq(field, "SupplementaryGroups")) {
- const char *p;
+ r = sd_bus_message_open_container(m, 'v', "(bas)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'r', "bas");
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_open_container(m, 'v', "as");
+ r = sd_bus_message_append_basic(m, 'b', &whitelist);
if (r < 0)
return bus_log_create_error(r);
@@ -1037,15 +986,12 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
if (r == 0)
break;
-
- if (!valid_user_group_name_or_id(word)) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_error_errno(r, "Invalid syntax: %s", eq);
r = sd_bus_message_append_basic(m, 's', word);
if (r < 0)
@@ -1057,50 +1003,21 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
return bus_log_create_error(r);
r = sd_bus_message_close_container(m);
-
- } else if (STR_IN_SET(field, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
- mode_t mode;
-
- r = parse_mode(eq, &mode);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s", field, eq);
-
- r = sd_bus_message_append(m, "v", "u", mode);
-
- } else if (STR_IN_SET(field, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
- const char *p;
-
- r = sd_bus_message_open_container(m, 'v', "as");
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_open_container(m, 'a', "s");
+ r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
- for (p = eq;;) {
- _cleanup_free_ char *word = NULL;
-
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s", field, eq);
- if (r == 0)
- break;
-
- r = sd_bus_message_append_basic(m, 's', word);
- if (r < 0)
- return bus_log_create_error(r);
- }
-
r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_close_container(m);
+ return 1;
+ }
- } else if (streq(field, "RestrictNamespaces")) {
+ if (streq(field, "RestrictNamespaces")) {
bool invert = false;
unsigned long flags = 0;
@@ -1123,27 +1040,31 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
if (invert)
flags = (~flags) & NAMESPACE_FLAGS_ALL;
- r = sd_bus_message_append(m, "v", "t", (uint64_t) flags);
- } else if ((dep = unit_dependency_from_string(field)) >= 0)
- r = sd_bus_message_append(m, "v", "as", 1, eq);
- else if (streq(field, "MountFlags")) {
- unsigned long f;
-
- r = mount_propagation_flags_from_string(eq, &f);
+ r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags);
if (r < 0)
- return log_error_errno(r, "Failed to parse mount propagation flags: %s", eq);
+ return bus_log_create_error(r);
- r = sd_bus_message_append(m, "v", "t", (uint64_t) f);
- } else if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
+ return 1;
+ }
+
+ if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
const char *p = eq;
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ if (r < 0)
+ return bus_log_create_error(r);
+
r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
if (r < 0)
- return r;
+ return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "(ssbt)");
if (r < 0)
- return r;
+ return bus_log_create_error(r);
for (;;) {
_cleanup_free_ char *source = NULL, *destination = NULL;
@@ -1196,137 +1117,528 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
if (r < 0)
- return r;
+ return bus_log_create_error(r);
}
r = sd_bus_message_close_container(m);
if (r < 0)
- return r;
+ return bus_log_create_error(r);
r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- } else if (STR_IN_SET(field, "ExecStartPre", "ExecStart", "ExecStartPost",
- "ExecReload", "ExecStop", "ExecStopPost")) {
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- bool ignore_failure = false, explicit_path = false, done = false;
- _cleanup_strv_free_ char **l = NULL;
- _cleanup_free_ char *path = NULL;
+ return 1;
+ }
- do {
- switch (*eq) {
+ return 0;
+}
- case '-':
- if (ignore_failure)
- done = true;
- else {
- ignore_failure = true;
- eq++;
- }
- break;
+static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
- case '@':
- if (explicit_path)
- done = true;
- else {
- explicit_path = true;
- eq++;
- }
- break;
+ if (streq(field, "KillMode"))
- case '+':
- case '!':
- /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
- log_error("Sorry, but +, ! and !! are currently not supported for transient services.");
- return -EOPNOTSUPP;
+ return bus_append_string(m, field, eq);
- default:
- done = true;
- break;
- }
- } while (!done);
+ if (STR_IN_SET(field, "SendSIGHUP", "SendSIGKILL"))
- if (explicit_path) {
- r = extract_first_word(&eq, &path, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
- if (r < 0)
- return log_error_errno(r, "Failed to parse path: %m");
- }
+ return bus_append_parse_boolean(m, field, eq);
- r = strv_split_extract(&l, eq, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
- if (r < 0)
- return log_error_errno(r, "Failed to parse command line: %m");
+ if (streq(field, "KillSignal"))
+
+ return bus_append_signal_from_string_try_harder(m, field, eq);
+
+ return 0;
+}
+
+static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
+
+ if (STR_IN_SET(field, "What", "Where", "Options", "Type"))
+
+ return bus_append_string(m, field, eq);
+
+ if (streq(field, "TimeoutSec"))
+
+ return bus_append_parse_sec_rename(m, field, eq);
+
+ if (streq(field, "DirectoryMode"))
+
+ return bus_append_parse_mode(m, field, eq);
+
+ if (STR_IN_SET(field, "SloppyOptions", "LazyUnmount", "ForceUnmount"))
+
+ return bus_append_parse_boolean(m, field, eq);
+
+ return 0;
+}
+
+static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
+ int r;
+
+ if (streq(field, "MakeDirectory"))
+
+ return bus_append_parse_boolean(m, field, eq);
+
+ if (streq(field, "DirectoryMode"))
+
+ return bus_append_parse_mode(m, field, eq);
+
+ if (STR_IN_SET(field,
+ "PathExists", "PathExistsGlob", "PathChanged",
+ "PathModified", "DirectoryNotEmpty")) {
- r = sd_bus_message_open_container(m, 'v', "a(sasb)");
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
+ else
+ r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq);
if (r < 0)
- return r;
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
+ int r;
+
+ if (STR_IN_SET(field,
+ "PIDFile", "Type", "Restart", "BusName", "NotifyAccess",
+ "USBFunctionDescriptors", "USBFunctionStrings"))
+
+ return bus_append_string(m, field, eq);
+
+ if (STR_IN_SET(field, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID"))
+
+ return bus_append_parse_boolean(m, field, eq);
- r = sd_bus_message_open_container(m, 'a', "(sasb)");
+ if (STR_IN_SET(field, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec"))
+
+ return bus_append_parse_sec_rename(m, field, eq);
+
+ if (streq(field, "TimeoutSec")) {
+
+ r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
if (r < 0)
return r;
- if (strv_length(l) > 0) {
+ return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq);
+ }
- r = sd_bus_message_open_container(m, 'r', "sasb");
- if (r < 0)
- return r;
+ if (streq(field, "FileDescriptorStoreMax"))
- r = sd_bus_message_append(m, "s", path ?: l[0]);
- if (r < 0)
- return r;
+ return bus_append_safe_atou(m, field, eq);
- r = sd_bus_message_append_strv(m, l);
- if (r < 0)
- return r;
+ if (STR_IN_SET(field,
+ "ExecStartPre", "ExecStart", "ExecStartPost",
+ "ExecReload", "ExecStop", "ExecStopPost"))
- r = sd_bus_message_append(m, "b", ignore_failure);
- if (r < 0)
- return r;
+ return bus_append_exec_command(m, field, eq);
+
+ if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
+ _cleanup_free_ int *status = NULL, *signal = NULL;
+ size_t sz_status = 0, sz_signal = 0;
+ const char *p;
- r = sd_bus_message_close_container(m);
+ for (p = eq;;) {
+ _cleanup_free_ char *word = NULL;
+ int val;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
if (r < 0)
- return r;
+ return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
+
+ r = safe_atoi(word, &val);
+ if (r < 0) {
+ val = signal_from_string_try_harder(word);
+ if (val < 0)
+ return log_error_errno(r, "Invalid status or signal %s in %s: %m", word, field);
+
+ signal = realloc_multiply(signal, sizeof(int), sz_signal + 1);
+ if (!signal)
+ return log_oom();
+
+ signal[sz_signal++] = val;
+ } else {
+ status = realloc_multiply(status, sizeof(int), sz_status + 1);
+ if (!status)
+ return log_oom();
+
+ status[sz_status++] = val;
+ }
}
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'v', "(aiai)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'r', "aiai");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_array(m, 'i', status, sz_status);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_array(m, 'i', signal, sz_signal);
+ if (r < 0)
+ return bus_log_create_error(r);
+
r = sd_bus_message_close_container(m);
if (r < 0)
- return r;
+ return bus_log_create_error(r);
r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) {
+ int r;
+
+ if (STR_IN_SET(field,
+ "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
+ "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
+
+ return bus_append_parse_boolean(m, field, eq);
+
+ if (STR_IN_SET(field, "Priority", "IPTTL", "Mark"))
+
+ return bus_append_safe_atoi(m, field, eq);
+
+ if (streq(field, "IPTOS"))
+
+ return bus_append_ip_tos_from_string(m, field, eq);
+
+ if (STR_IN_SET(field, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
- } else if (STR_IN_SET(field,
- "OnActiveSec", "OnBootSec", "OnStartupSec",
- "OnUnitActiveSec","OnUnitInactiveSec")) {
- usec_t t;
+ return bus_append_safe_atou(m, field, eq);
- r = parse_sec(eq, &t);
+ if (STR_IN_SET(field, "SocketMode", "DirectoryMode"))
+
+ return bus_append_parse_mode(m, field, eq);
+
+ if (STR_IN_SET(field, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
+
+ return bus_append_safe_atoi64(m, field, eq);
+
+ if (STR_IN_SET(field, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
+
+ return bus_append_parse_sec_rename(m, field, eq);
+
+ if (STR_IN_SET(field, "ReceiveBuffer", "SendBuffer", "PipeSize"))
+
+ return bus_append_parse_size(m, field, eq, 1024);
+
+ if (STR_IN_SET(field, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
+
+ return bus_append_exec_command(m, field, eq);
+
+ if (STR_IN_SET(field,
+ "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
+ "BindToDevice", "BindIPv6Only", "FileDescriptorName",
+ "SocketUser", "SocketGroup"))
+
+ return bus_append_string(m, field, eq);
+
+ if (streq(field, "Symlinks"))
+
+ return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
+
+ if (streq(field, "SocketProtocol"))
+
+ return bus_append_socket_protocol_from_name(m, field, eq);
+
+ if (STR_IN_SET(field,
+ "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
+ "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
+ else
+ r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq);
if (r < 0)
- return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
+ return bus_log_create_error(r);
- r = sd_bus_message_append(m, "v", "t", t);
+ return 1;
+ }
- } else {
- log_error("Unknown assignment: %s", assignment);
- return -EINVAL;
+ return 0;
+}
+static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
+ int r;
+
+ if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent"))
+
+ return bus_append_parse_boolean(m, field, eq);
+
+ if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec"))
+
+ return bus_append_parse_sec_rename(m, field, eq);
+
+ if (STR_IN_SET(field,
+ "OnActiveSec", "OnBootSec", "OnStartupSec",
+ "OnUnitActiveSec","OnUnitInactiveSec")) {
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
+ else {
+ usec_t t;
+ r = parse_sec(eq, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
+
+ r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t);
+ }
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
}
-finish:
- if (r < 0)
- return bus_log_create_error(r);
+ if (streq(field, "OnCalendar")) {
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
+ else
+ r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
+ ConditionType t = _CONDITION_TYPE_INVALID;
+ bool is_condition = false;
+ int r;
+
+ if (STR_IN_SET(field,
+ "Description", "SourcePath", "OnFailureJobMode",
+ "JobTimeoutAction", "JobTimeoutRebootArgument",
+ "StartLimitAction", "FailureAction", "SuccessAction",
+ "RebootArgument", "CollectMode"))
+
+ return bus_append_string(m, field, eq);
+
+ if (STR_IN_SET(field,
+ "StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop",
+ "AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies"))
+
+ return bus_append_parse_boolean(m, field, eq);
+
+ if (STR_IN_SET(field, "JobTimeoutSec", "JobRunningTimeoutSec", "StartLimitIntervalSec"))
+
+ return bus_append_parse_sec_rename(m, field, eq);
+
+ if (streq(field, "StartLimitBurst"))
+
+ return bus_append_safe_atou(m, field, eq);
+
+ if (unit_dependency_from_string(field) >= 0 ||
+ STR_IN_SET(field, "Documentation", "RequiresMountsFor"))
+
+ return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
+
+ t = condition_type_from_string(field);
+ if (t >= 0)
+ is_condition = true;
+ else
+ t = assert_type_from_string(field);
+ if (t >= 0) {
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0);
+ else {
+ const char *p = eq;
+ int trigger, negate;
+
+ trigger = *p == '|';
+ if (trigger)
+ p++;
+
+ negate = *p == '!';
+ if (negate)
+ p++;
+
+ r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1,
+ field, trigger, negate, p);
+ }
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
return 0;
}
-int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l) {
+int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) {
+ const char *eq, *field;
+ int r;
+
+ assert(m);
+ assert(assignment);
+
+ eq = strchr(assignment, '=');
+ if (!eq) {
+ log_error("Not an assignment: %s", assignment);
+ return -EINVAL;
+ }
+
+ field = strndupa(assignment, eq - assignment);
+ eq++;
+
+ switch (t) {
+ case UNIT_SERVICE:
+ r = bus_append_cgroup_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ r = bus_append_execute_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ r = bus_append_kill_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ r = bus_append_service_property(m, field, eq);
+ if (r != 0)
+ return r;
+ break;
+
+ case UNIT_SOCKET:
+ r = bus_append_cgroup_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ r = bus_append_execute_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ r = bus_append_kill_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ r = bus_append_socket_property(m, field, eq);
+ if (r != 0)
+ return r;
+ break;
+
+ case UNIT_TIMER:
+ r = bus_append_timer_property(m, field, eq);
+ if (r != 0)
+ return r;
+ break;
+
+ case UNIT_PATH:
+ r = bus_append_path_property(m, field, eq);
+ if (r != 0)
+ return r;
+ break;
+
+ case UNIT_SLICE:
+ r = bus_append_cgroup_property(m, field, eq);
+ if (r != 0)
+ return r;
+ break;
+
+ case UNIT_SCOPE:
+
+ if (streq(field, "TimeoutStopSec"))
+ return bus_append_parse_sec_rename(m, field, eq);
+
+ r = bus_append_cgroup_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ r = bus_append_kill_property(m, field, eq);
+ if (r != 0)
+ return r;
+ break;
+
+ case UNIT_MOUNT:
+ r = bus_append_cgroup_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ r = bus_append_execute_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ r = bus_append_kill_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ r = bus_append_mount_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ break;
+
+ case UNIT_AUTOMOUNT:
+ r = bus_append_automount_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ break;
+
+ case UNIT_TARGET:
+ case UNIT_DEVICE:
+ case UNIT_SWAP:
+ log_error("Not supported unit type");
+ return -EINVAL;
+
+ default:
+ log_error("Invalid unit type");
+ return -EINVAL;
+ }
+
+ r = bus_append_unit_property(m, field, eq);
+ if (r != 0)
+ return r;
+
+ log_error("Unknown assignment: %s", assignment);
+ return -EINVAL;
+}
+
+int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
char **i;
int r;
assert(m);
STRV_FOREACH(i, l) {
- r = bus_append_unit_property_assignment(m, *i);
+ r = bus_append_unit_property_assignment(m, t, *i);
if (r < 0)
return r;
}
@@ -1418,31 +1730,25 @@ int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
/* When we are a bus client we match by sender. Direct
* connections OTOH have no initialized sender field, and
* hence we ignore the sender then */
- r = sd_bus_add_match(
+ r = sd_bus_match_signal_async(
bus,
&d->slot_job_removed,
- bus->bus_client ?
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='JobRemoved',"
- "path='/org/freedesktop/systemd1'" :
- "type='signal',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='JobRemoved',"
- "path='/org/freedesktop/systemd1'",
- match_job_removed, d);
+ bus->bus_client ? "org.freedesktop.systemd1" : NULL,
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "JobRemoved",
+ match_job_removed, NULL, d);
if (r < 0)
return r;
- r = sd_bus_add_match(
+ r = sd_bus_match_signal_async(
bus,
&d->slot_disconnected,
- "type='signal',"
- "sender='org.freedesktop.DBus.Local',"
- "interface='org.freedesktop.DBus.Local',"
- "member='Disconnected'",
- match_disconnected, d);
+ "org.freedesktop.DBus.Local",
+ NULL,
+ "org.freedesktop.DBus.Local",
+ "Disconnected",
+ match_disconnected, NULL, d);
if (r < 0)
return r;
diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h
index 1a137e8b84..514e6edb76 100644
--- a/src/shared/bus-unit-util.h
+++ b/src/shared/bus-unit-util.h
@@ -20,10 +20,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-bus.h"
-
-#include "output-mode.h"
#include "install.h"
+#include "output-mode.h"
+#include "sd-bus.h"
+#include "unit-def.h"
typedef struct UnitInfo {
const char *machine;
@@ -41,8 +41,8 @@ typedef struct UnitInfo {
int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);
-int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment);
-int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l);
+int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment);
+int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l);
typedef struct BusWaitForJobs BusWaitForJobs;
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 7a185461a3..548e817105 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -68,7 +68,7 @@ static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_
}
int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
- _cleanup_free_ char *match = NULL;
+ const char *match;
const char *unique;
int r;
@@ -85,23 +85,21 @@ int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
if (r < 0)
return r;
- r = asprintf(&match,
- "sender='org.freedesktop.DBus',"
- "type='signal',"
- "interface='org.freedesktop.DBus',"
- "member='NameOwnerChanged',"
- "path='/org/freedesktop/DBus',"
- "arg0='%s',"
- "arg1='%s',"
- "arg2=''", name, unique);
- if (r < 0)
- return -ENOMEM;
-
- r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e);
+ match = strjoina(
+ "sender='org.freedesktop.DBus',"
+ "type='signal',"
+ "interface='org.freedesktop.DBus',"
+ "member='NameOwnerChanged',"
+ "path='/org/freedesktop/DBus',"
+ "arg0='", name, "',",
+ "arg1='", unique, "',",
+ "arg2=''");
+
+ r = sd_bus_add_match_async(bus, NULL, match, name_owner_change_callback, NULL, e);
if (r < 0)
return r;
- r = sd_bus_release_name(bus, name);
+ r = sd_bus_release_name_async(bus, NULL, name, NULL, NULL);
if (r < 0)
return r;
@@ -560,8 +558,7 @@ void bus_verify_polkit_async_registry_free(Hashmap *registry) {
int bus_check_peercred(sd_bus *c) {
struct ucred ucred;
- socklen_t l;
- int fd;
+ int fd, r;
assert(c);
@@ -569,12 +566,9 @@ int bus_check_peercred(sd_bus *c) {
if (fd < 0)
return fd;
- l = sizeof(struct ucred);
- if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
- return -errno;
-
- if (l != sizeof(struct ucred))
- return -E2BIG;
+ r = getpeercred(fd, &ucred);
+ if (r < 0)
+ return r;
if (ucred.uid != 0 && ucred.uid != geteuid())
return -EPERM;
@@ -717,7 +711,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool value, b
/* Yes, heuristics! But we can change this check
* should it turn out to not be sufficient */
- if (endswith(name, "Timestamp")) {
+ if (endswith(name, "Timestamp") || STR_IN_SET(name, "NextElapseUSecRealtime", "LastTriggerUSec")) {
char timestamp[FORMAT_TIMESTAMP_MAX], *t;
t = format_timestamp(timestamp, sizeof(timestamp), u);
@@ -1344,6 +1338,25 @@ int bus_property_get_bool(
return sd_bus_message_append_basic(reply, 'b', &b);
}
+int bus_property_set_bool(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *value,
+ void *userdata,
+ sd_bus_error *error) {
+
+ int b, r;
+
+ r = sd_bus_message_read(value, "b", &b);
+ if (r < 0)
+ return r;
+
+ *(bool *) userdata = !!b;
+ return 0;
+}
+
int bus_property_get_id128(
sd_bus *bus,
const char *path,
@@ -1604,3 +1617,54 @@ int bus_track_add_name_many(sd_bus_track *t, char **l) {
return r;
}
+
+int bus_open_system_watch_bind(sd_bus **ret) {
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ const char *e;
+ int r;
+
+ assert(ret);
+
+ /* Match like sd_bus_open_system(), but with the "watch_bind" feature and the Connected() signal turned on. */
+
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS");
+ if (!e)
+ e = DEFAULT_SYSTEM_BUS_ADDRESS;
+
+ r = sd_bus_set_address(bus, e);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_set_bus_client(bus, true);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_set_trusted(bus, true);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_negotiate_creds(bus, true, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_set_watch_bind(bus, true);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_set_connected_signal(bus, true);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_start(bus);
+ if (r < 0)
+ return r;
+
+ *ret = bus;
+ bus = NULL;
+
+ return 0;
+}
diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
index a9f4969d7d..969a444d83 100644
--- a/src/shared/bus-util.h
+++ b/src/shared/bus-util.h
@@ -80,6 +80,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool value, b
int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all);
int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+int bus_property_set_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *error);
int bus_property_get_id128(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
#define bus_property_get_usec ((sd_bus_property_get_t) NULL)
@@ -162,3 +163,5 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send
int bus_property_get_rlimit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
int bus_track_add_name_many(sd_bus_track *t, char **l);
+
+int bus_open_system_watch_bind(sd_bus **ret);
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 3f32dfb7b6..a2fd05c425 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -26,6 +26,7 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/utsname.h>
#include <time.h>
#include <unistd.h>
@@ -36,6 +37,7 @@
#include "architecture.h"
#include "audit-util.h"
#include "cap-list.h"
+#include "cgroup-util.h"
#include "condition.h"
#include "extract-word.h"
#include "fd-util.h"
@@ -142,6 +144,70 @@ static int condition_test_kernel_command_line(Condition *c) {
return false;
}
+static int condition_test_kernel_version(Condition *c) {
+ enum {
+ /* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
+ * should be listed first. */
+ LOWER_OR_EQUAL,
+ GREATER_OR_EQUAL,
+ LOWER,
+ GREATER,
+ EQUAL,
+ _ORDER_MAX,
+ };
+
+ static const char *const prefix[_ORDER_MAX] = {
+ [LOWER_OR_EQUAL] = "<=",
+ [GREATER_OR_EQUAL] = ">=",
+ [LOWER] = "<",
+ [GREATER] = ">",
+ [EQUAL] = "=",
+ };
+ const char *p = NULL;
+ struct utsname u;
+ size_t i;
+ int k;
+
+ assert(c);
+ assert(c->parameter);
+ assert(c->type == CONDITION_KERNEL_VERSION);
+
+ assert_se(uname(&u) >= 0);
+
+ for (i = 0; i < _ORDER_MAX; i++) {
+ p = startswith(c->parameter, prefix[i]);
+ if (p)
+ break;
+ }
+
+ /* No prefix? Then treat as glob string */
+ if (!p)
+ return fnmatch(skip_leading_chars(c->parameter, NULL), u.release, 0) == 0;
+
+ k = str_verscmp(u.release, skip_leading_chars(p, NULL));
+
+ switch (i) {
+
+ case LOWER:
+ return k < 0;
+
+ case LOWER_OR_EQUAL:
+ return k <= 0;
+
+ case EQUAL:
+ return k == 0;
+
+ case GREATER_OR_EQUAL:
+ return k >= 0;
+
+ case GREATER:
+ return k > 0;
+
+ default:
+ assert_not_reached("Can't compare");
+ }
+}
+
static int condition_test_user(Condition *c) {
uid_t id;
int r;
@@ -177,6 +243,30 @@ static int condition_test_user(Condition *c) {
return id == getuid() || id == geteuid();
}
+static int condition_test_control_group_controller(Condition *c) {
+ int r;
+ CGroupMask system_mask, wanted_mask = 0;
+
+ assert(c);
+ assert(c->parameter);
+ assert(c->type == CONDITION_CONTROL_GROUP_CONTROLLER);
+
+ r = cg_mask_supported(&system_mask);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to determine supported controllers: %m");
+
+ r = cg_mask_from_string(c->parameter, &wanted_mask);
+ if (r < 0 || wanted_mask <= 0) {
+ /* This won't catch the case that we have an unknown controller
+ * mixed in with valid ones -- these are only assessed on the
+ * validity of the valid controllers found. */
+ log_debug("Failed to parse cgroup string: %s", c->parameter);
+ return 1;
+ }
+
+ return (system_mask & wanted_mask) == wanted_mask;
+}
+
static int condition_test_group(Condition *c) {
gid_t id;
int r;
@@ -527,6 +617,7 @@ int condition_test(Condition *c) {
[CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
[CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
[CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
+ [CONDITION_KERNEL_VERSION] = condition_test_kernel_version,
[CONDITION_VIRTUALIZATION] = condition_test_virtualization,
[CONDITION_SECURITY] = condition_test_security,
[CONDITION_CAPABILITY] = condition_test_capability,
@@ -537,6 +628,7 @@ int condition_test(Condition *c) {
[CONDITION_FIRST_BOOT] = condition_test_first_boot,
[CONDITION_USER] = condition_test_user,
[CONDITION_GROUP] = condition_test_group,
+ [CONDITION_CONTROL_GROUP_CONTROLLER] = condition_test_control_group_controller,
[CONDITION_NULL] = condition_test_null,
};
@@ -561,8 +653,7 @@ void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_
assert(c);
assert(f);
- if (!prefix)
- prefix = "";
+ prefix = strempty(prefix);
fprintf(f,
"%s\t%s: %s%s%s %s\n",
@@ -586,6 +677,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
[CONDITION_HOST] = "ConditionHost",
[CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
+ [CONDITION_KERNEL_VERSION] = "ConditionKernelVersion",
[CONDITION_SECURITY] = "ConditionSecurity",
[CONDITION_CAPABILITY] = "ConditionCapability",
[CONDITION_AC_POWER] = "ConditionACPower",
@@ -602,6 +694,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
[CONDITION_USER] = "ConditionUser",
[CONDITION_GROUP] = "ConditionGroup",
+ [CONDITION_CONTROL_GROUP_CONTROLLER] = "ConditionControlGroupController",
[CONDITION_NULL] = "ConditionNull"
};
@@ -612,6 +705,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_VIRTUALIZATION] = "AssertVirtualization",
[CONDITION_HOST] = "AssertHost",
[CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
+ [CONDITION_KERNEL_VERSION] = "AssertKernelVersion",
[CONDITION_SECURITY] = "AssertSecurity",
[CONDITION_CAPABILITY] = "AssertCapability",
[CONDITION_AC_POWER] = "AssertACPower",
@@ -628,6 +722,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
[CONDITION_USER] = "AssertUser",
[CONDITION_GROUP] = "AssertGroup",
+ [CONDITION_CONTROL_GROUP_CONTROLLER] = "AssertControlGroupController",
[CONDITION_NULL] = "AssertNull"
};
diff --git a/src/shared/condition.h b/src/shared/condition.h
index 534906b6d6..a84d993370 100644
--- a/src/shared/condition.h
+++ b/src/shared/condition.h
@@ -31,6 +31,7 @@ typedef enum ConditionType {
CONDITION_VIRTUALIZATION,
CONDITION_HOST,
CONDITION_KERNEL_COMMAND_LINE,
+ CONDITION_KERNEL_VERSION,
CONDITION_SECURITY,
CONDITION_CAPABILITY,
CONDITION_AC_POWER,
@@ -53,6 +54,8 @@ typedef enum ConditionType {
CONDITION_USER,
CONDITION_GROUP,
+ CONDITION_CONTROL_GROUP_CONTROLLER,
+
_CONDITION_TYPE_MAX,
_CONDITION_TYPE_INVALID = -1
} ConditionType;
@@ -96,3 +99,17 @@ ConditionType assert_type_from_string(const char *s) _pure_;
const char* condition_result_to_string(ConditionResult r) _const_;
ConditionResult condition_result_from_string(const char *s) _pure_;
+
+static inline bool condition_takes_path(ConditionType t) {
+ return IN_SET(t,
+ CONDITION_PATH_EXISTS,
+ CONDITION_PATH_EXISTS_GLOB,
+ CONDITION_PATH_IS_DIRECTORY,
+ CONDITION_PATH_IS_SYMBOLIC_LINK,
+ CONDITION_PATH_IS_MOUNT_POINT,
+ CONDITION_PATH_IS_READ_WRITE,
+ CONDITION_DIRECTORY_NOT_EMPTY,
+ CONDITION_FILE_NOT_EMPTY,
+ CONDITION_FILE_IS_EXECUTABLE,
+ CONDITION_NEEDS_UPDATE);
+}
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index e8d7db8f0c..86114e3dd1 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -22,9 +22,12 @@
#include <sys/prctl.h>
#include <sys/wait.h>
+#include "sd-id128.h"
+
#include "architecture.h"
#include "ask-password-api.h"
#include "blkid-util.h"
+#include "blockdev-util.h"
#include "copy.h"
#include "crypt-util.h"
#include "def.h"
@@ -38,6 +41,7 @@
#include "hostname-util.h"
#include "id128-util.h"
#include "linux-3.13/dm-ioctl.h"
+#include "missing.h"
#include "mount-util.h"
#include "path-util.h"
#include "process-util.h"
@@ -351,7 +355,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
/* Filter out weird MMC RPMB partitions, which cannot reasonably be read, see
* https://github.com/systemd/systemd/issues/5806 */
sysname = udev_device_get_sysname(q);
- if (sysname && startswith(sysname, "mmcblk") && endswith(sysname, "rpmb"))
+ if (sysname && startswith(sysname, "mmcblk") &&
+ (endswith(sysname, "rpmb") || endswith(sysname, "boot0" ) || endswith(sysname, "boot1")))
continue;
node = udev_device_get_devnode(q);
@@ -1242,7 +1247,6 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
_cleanup_free_ char *hostname = NULL;
unsigned n_meta_initialized = 0, k;
int fds[2 * _META_MAX], r;
- siginfo_t si;
BLOCK_SIGNALS(SIGCHLD);
@@ -1258,18 +1262,10 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
if (r < 0)
goto finish;
- child = raw_clone(SIGCHLD|CLONE_NEWNS);
- if (child < 0) {
- r = -errno;
+ r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS, &child);
+ if (r < 0)
goto finish;
- }
-
- if (child == 0) {
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
+ if (r == 0) {
/* Make sure we never propagate to the host */
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
_exit(EXIT_FAILURE);
@@ -1364,15 +1360,12 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
}
}
- r = wait_for_terminate(child, &si);
- if (r < 0)
- goto finish;
+ r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
child = 0;
-
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) {
- r = -EPROTO;
+ if (r < 0)
goto finish;
- }
+ if (r != EXIT_SUCCESS)
+ return -EPROTO;
free_and_replace(m->hostname, hostname);
m->machine_id = machine_id;
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
index 53a1554a28..10e251ff09 100644
--- a/src/shared/dissect-image.h
+++ b/src/shared/dissect-image.h
@@ -22,6 +22,8 @@
#include <stdbool.h>
+#include "sd-id128.h"
+
#include "macro.h"
typedef struct DissectedImage DissectedImage;
diff --git a/src/shared/efivars.h b/src/shared/efivars.h
index 9a4880de7d..5b5951321c 100644
--- a/src/shared/efivars.h
+++ b/src/shared/efivars.h
@@ -20,6 +20,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#if ! ENABLE_EFI
+#include <errno.h>
+#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
diff --git a/src/shared/generator.c b/src/shared/generator.c
index 3495a7ef7d..2b0a4ecdc8 100644
--- a/src/shared/generator.c
+++ b/src/shared/generator.c
@@ -19,6 +19,7 @@
***/
#include <errno.h>
+#include <stdio_ext.h>
#include <unistd.h>
#include "alloc-util.h"
@@ -39,6 +40,39 @@
#include "unit-name.h"
#include "util.h"
+int generator_open_unit_file(
+ const char *dest,
+ const char *source,
+ const char *name,
+ FILE **file) {
+
+ const char *unit;
+ FILE *f;
+
+ unit = strjoina(dest, "/", name);
+
+ f = fopen(unit, "wxe");
+ if (!f) {
+ if (source && errno == EEXIST)
+ return log_error_errno(errno,
+ "Failed to create unit file %s, as it already exists. Duplicate entry in %s?",
+ unit, source);
+ else
+ return log_error_errno(errno,
+ "Failed to create unit file %s: %m",
+ unit);
+ }
+
+ (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+
+ fprintf(f,
+ "# Automatically generated by %s\n\n",
+ program_invocation_short_name);
+
+ *file = f;
+ return 0;
+}
+
int generator_add_symlink(const char *root, const char *dst, const char *dep_type, const char *src) {
/* Adds a symlink from <dst>.<dep_type>.d/ to ../<src> */
diff --git a/src/shared/generator.h b/src/shared/generator.h
index 32d1ad021c..e1c636a218 100644
--- a/src/shared/generator.h
+++ b/src/shared/generator.h
@@ -22,6 +22,12 @@
#include <stdio.h>
+int generator_open_unit_file(
+ const char *dest,
+ const char *source,
+ const char *name,
+ FILE **file);
+
int generator_add_symlink(const char *root, const char *dst, const char *dep_type, const char *src);
int generator_write_fsck_deps(
diff --git a/src/shared/install.c b/src/shared/install.c
index 05ccc586a9..026aa32302 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -2784,6 +2784,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
if (scope == UNIT_FILE_SYSTEM)
r = conf_files_list(&files, ".preset", root_dir, 0,
"/etc/systemd/system-preset",
+ "/run/systemd/system-preset",
"/usr/local/lib/systemd/system-preset",
"/usr/lib/systemd/system-preset",
#if HAVE_SPLIT_USR
@@ -2793,6 +2794,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
else if (scope == UNIT_FILE_GLOBAL)
r = conf_files_list(&files, ".preset", root_dir, 0,
"/etc/systemd/user-preset",
+ "/run/systemd/user-preset",
"/usr/local/lib/systemd/user-preset",
"/usr/lib/systemd/user-preset",
NULL);
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index afc3dcd219..5609e42feb 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -166,8 +166,17 @@ static bool shall_print(const char *p, size_t l, OutputFlags flags) {
return true;
}
-static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputFlags flags, int priority, const char* message, size_t message_len) {
- const char *color_on = "", *color_off = "";
+static bool print_multiline(
+ FILE *f,
+ unsigned prefix,
+ unsigned n_columns,
+ OutputFlags flags,
+ int priority,
+ const char* message,
+ size_t message_len,
+ size_t highlight[2]) {
+
+ const char *color_on = "", *color_off = "", *highlight_on = "";
const char *pos, *end;
bool ellipsized = false;
int line = 0;
@@ -176,9 +185,11 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output
if (priority <= LOG_ERR) {
color_on = ANSI_HIGHLIGHT_RED;
color_off = ANSI_NORMAL;
+ highlight_on = ANSI_HIGHLIGHT;
} else if (priority <= LOG_NOTICE) {
color_on = ANSI_HIGHLIGHT;
color_off = ANSI_NORMAL;
+ highlight_on = ANSI_HIGHLIGHT_RED;
}
}
@@ -209,9 +220,28 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output
if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) ||
(prefix + len + 1 < n_columns && !tail_line)) {
- fprintf(f, "%*s%s%.*s%s\n",
- continuation * prefix, "",
- color_on, len, pos, color_off);
+ if (highlight &&
+ (size_t) (pos - message) <= highlight[0] &&
+ highlight[0] < (size_t) len) {
+
+ fprintf(f, "%*s%s%.*s",
+ continuation * prefix, "",
+ color_on, (int) highlight[0], pos);
+ fprintf(f, "%s%.*s",
+ highlight_on,
+ (int) (MIN((size_t) len, highlight[1]) - highlight[0]),
+ pos + highlight[0]);
+ if ((size_t) len > highlight[1])
+ fprintf(f, "%s%.*s",
+ color_on,
+ (int) (len - highlight[1]),
+ pos + highlight[1]);
+ fprintf(f, "%s\n", color_off);
+
+ } else
+ fprintf(f, "%*s%s%.*s%s\n",
+ continuation * prefix, "",
+ color_on, len, pos, color_off);
continue;
}
@@ -369,7 +399,8 @@ static int output_short(
OutputMode mode,
unsigned n_columns,
OutputFlags flags,
- Set *output_fields) {
+ Set *output_fields,
+ size_t highlight[2]) {
int r;
const void *data;
@@ -390,6 +421,7 @@ static int output_short(
PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len),
PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len),
};
+ size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0};
assert(f);
assert(j);
@@ -421,7 +453,7 @@ static int output_short(
}
if (!(flags & OUTPUT_SHOW_ALL))
- strip_tab_ansi(&message, &message_len);
+ strip_tab_ansi(&message, &message_len, highlight_shifted);
if (priority_len == 1 && *priority >= '0' && *priority <= '7')
p = *priority - '0';
@@ -468,7 +500,9 @@ static int output_short(
} else {
fputs(": ", f);
ellipsized |=
- print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
+ print_multiline(f, n + 2, n_columns, flags, p,
+ message, message_len,
+ highlight_shifted);
}
if (flags & OUTPUT_CATALOG)
@@ -483,7 +517,8 @@ static int output_verbose(
OutputMode mode,
unsigned n_columns,
OutputFlags flags,
- Set *output_fields) {
+ Set *output_fields,
+ size_t highlight[2]) {
const void *data;
size_t length;
@@ -561,7 +596,7 @@ static int output_verbose(
(((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
&& utf8_is_printable(data, length))) {
fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
- print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
+ print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1, NULL);
fputs(off, f);
} else {
char bytes[FORMAT_BYTES_MAX];
@@ -590,7 +625,8 @@ static int output_export(
OutputMode mode,
unsigned n_columns,
OutputFlags flags,
- Set *output_fields) {
+ Set *output_fields,
+ size_t highlight[2]) {
sd_id128_t boot_id;
char sid[33];
@@ -661,6 +697,10 @@ static int output_export(
fputc('\n', f);
}
+ if (r == -EBADMSG) {
+ log_debug_errno(r, "Skipping message we can't read: %m");
+ return 0;
+ }
if (r < 0)
return r;
@@ -728,7 +768,8 @@ static int output_json(
OutputMode mode,
unsigned n_columns,
OutputFlags flags,
- Set *output_fields) {
+ Set *output_fields,
+ size_t highlight[2]) {
uint64_t realtime, monotonic;
_cleanup_free_ char *cursor = NULL;
@@ -824,6 +865,11 @@ static int output_json(
}
}
+ if (r == -EBADMSG) {
+ log_debug_errno(r, "Skipping message we can't read: %m");
+ return 0;
+ }
+
if (r < 0)
return r;
@@ -952,18 +998,29 @@ static int output_cat(
OutputMode mode,
unsigned n_columns,
OutputFlags flags,
- Set *output_fields) {
+ Set *output_fields,
+ size_t highlight[2]) {
const void *data;
size_t l;
int r;
+ const char *highlight_on = "", *highlight_off = "";
assert(j);
assert(f);
+ if (flags & OUTPUT_COLOR) {
+ highlight_on = ANSI_HIGHLIGHT_RED;
+ highlight_off = ANSI_NORMAL;
+ }
+
sd_journal_set_data_threshold(j, 0);
r = sd_journal_get_data(j, "MESSAGE", &data, &l);
+ if (r == -EBADMSG) {
+ log_debug_errno(r, "Skipping message we can't read: %m");
+ return 0;
+ }
if (r < 0) {
/* An entry without MESSAGE=? */
if (r == -ENOENT)
@@ -974,7 +1031,17 @@ static int output_cat(
assert(l >= 8);
- fwrite((const char*) data + 8, 1, l - 8, f);
+ if (highlight && (flags & OUTPUT_COLOR)) {
+ assert(highlight[0] <= highlight[1]);
+ assert(highlight[1] <= l - 8);
+
+ fwrite((const char*) data + 8, 1, highlight[0], f);
+ fwrite(highlight_on, 1, strlen(highlight_on), f);
+ fwrite((const char*) data + 8 + highlight[0], 1, highlight[1] - highlight[0], f);
+ fwrite(highlight_off, 1, strlen(highlight_off), f);
+ fwrite((const char*) data + 8 + highlight[1], 1, l - 8 - highlight[1], f);
+ } else
+ fwrite((const char*) data + 8, 1, l - 8, f);
fputc('\n', f);
return 0;
@@ -986,7 +1053,8 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
OutputMode mode,
unsigned n_columns,
OutputFlags flags,
- Set *output_fields) = {
+ Set *output_fields,
+ size_t highlight[2]) = {
[OUTPUT_SHORT] = output_short,
[OUTPUT_SHORT_ISO] = output_short,
@@ -1010,6 +1078,7 @@ int output_journal(
unsigned n_columns,
OutputFlags flags,
char **output_fields,
+ size_t highlight[2],
bool *ellipsized) {
int ret;
@@ -1030,7 +1099,7 @@ int output_journal(
return ret;
}
- ret = output_funcs[mode](f, j, mode, n_columns, flags, fields);
+ ret = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight);
if (ellipsized && ret > 0)
*ellipsized = true;
@@ -1112,7 +1181,7 @@ static int show_journal(FILE *f,
line++;
maybe_print_begin_newline(f, &flags);
- r = output_journal(f, j, mode, n_columns, flags, NULL, ellipsized);
+ r = output_journal(f, j, mode, n_columns, flags, NULL, NULL, ellipsized);
if (r < 0)
return r;
}
@@ -1256,7 +1325,6 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
_cleanup_close_pair_ int pair[2] = { -1, -1 };
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
pid_t pid, child;
- siginfo_t si;
char buf[37];
ssize_t k;
int r;
@@ -1278,11 +1346,10 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- child = fork();
- if (child < 0)
- return -errno;
-
- if (child == 0) {
+ r = safe_fork("(sd-bootid)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ if (r < 0)
+ return r;
+ if (r == 0) {
int fd;
pair[0] = safe_close(pair[0]);
@@ -1309,9 +1376,11 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate(child, &si);
- if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
- return r < 0 ? r : -EIO;
+ r = wait_for_terminate_and_check("(sd-bootid)", child, 0);
+ if (r < 0)
+ return r;
+ if (r != EXIT_SUCCESS)
+ return -EIO;
k = recv(pair[0], buf, 36, 0);
if (k != 36)
@@ -1392,7 +1461,7 @@ int show_journal_by_unit(
if (r < 0)
return log_error_errno(r, "Failed to add unit matches: %m");
- if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
+ if (DEBUG_LOGGING) {
_cleanup_free_ char *filter;
filter = journal_make_match_string(j);
diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h
index eaa69b6e90..c876dcc46a 100644
--- a/src/shared/logs-show.h
+++ b/src/shared/logs-show.h
@@ -39,6 +39,7 @@ int output_journal(
unsigned n_columns,
OutputFlags flags,
char **output_fields,
+ size_t highlight[2],
bool *ellipsized);
int add_match_this_boot(sd_journal *j, const char *machine);
diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c
index 097de690e5..37b8479f88 100644
--- a/src/shared/loop-util.c
+++ b/src/shared/loop-util.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <fcntl.h>
#include <linux/loop.h>
#include <sys/ioctl.h>
diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c
index 167bcfad36..031d443e9e 100644
--- a/src/shared/machine-pool.c
+++ b/src/shared/machine-pool.c
@@ -42,6 +42,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "label.h"
#include "lockfile-util.h"
#include "log.h"
#include "machine-pool.h"
@@ -78,7 +79,6 @@ static int setup_machine_raw(uint64_t size, sd_bus_error *error) {
_cleanup_close_ int fd = -1;
struct statvfs ss;
pid_t pid = 0;
- siginfo_t si;
int r;
/* We want to be able to make use of btrfs-specific file
@@ -122,20 +122,15 @@ static int setup_machine_raw(uint64_t size, sd_bus_error *error) {
goto fail;
}
- pid = fork();
- if (pid < 0) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m");
+ r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid);
+ if (r < 0) {
+ sd_bus_error_set_errnof(error, r, "Failed to fork mkfs.btrfs: %m");
goto fail;
}
-
- if (pid == 0) {
+ if (r == 0) {
/* Child */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
fd = safe_close(fd);
execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL);
@@ -145,24 +140,19 @@ static int setup_machine_raw(uint64_t size, sd_bus_error *error) {
_exit(EXIT_FAILURE);
}
- r = wait_for_terminate(pid, &si);
- if (r < 0) {
- sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m");
- goto fail;
- }
-
+ r = wait_for_terminate_and_check("mkfs", pid, 0);
pid = 0;
- if (si.si_code != CLD_EXITED) {
- r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally.");
+ if (r < 0) {
+ sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m");
goto fail;
}
- if (si.si_status == 99) {
+ if (r == 99) {
r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
goto fail;
}
- if (si.si_status != 0) {
- r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status);
+ if (r != EXIT_SUCCESS) {
+ r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", r);
goto fail;
}
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 06a944c49d..91fecf896c 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -56,8 +56,6 @@ shared_sources = '''
firewall-util.h
fstab-util.c
fstab-util.h
- gcrypt-util.c
- gcrypt-util.h
generator.c
generator.h
gpt.h
@@ -120,6 +118,7 @@ shared_sources = '''
volatile-util.h
watchdog.c
watchdog.h
+ wireguard-netlink.h
'''.split()
test_tables_h = files('test-tables.h')
@@ -159,24 +158,25 @@ libshared_deps = [threads,
libshared_sym_path = '@0@/libshared.sym'.format(meson.current_source_dir())
-libshared = shared_library(
+libshared_static = static_library(
libshared_name,
shared_sources,
- basic_sources,
- journal_internal_sources,
- libsystemd_internal_sources,
+ include_directories : includes,
+ dependencies : libshared_deps,
+ c_args : ['-fvisibility=default'])
+
+libshared = shared_library(
+ libshared_name,
libudev_sources,
include_directories : includes,
link_args : ['-shared',
- '-Wl,--version-script=' + libshared_sym_path],
+ '-Wl,--version-script=' + libshared_sym_path],
+ link_whole : [libshared_static,
+ libbasic,
+ libbasic_gcrypt,
+ libsystemd_static,
+ libjournal_client],
c_args : ['-fvisibility=default'],
dependencies : libshared_deps,
install : true,
install_dir : rootlibexecdir)
-
-libshared_static = static_library(
- libshared_name,
- shared_sources,
- basic_sources,
- include_directories : includes,
- dependencies : libshared_deps)
diff --git a/src/shared/nsflags.h b/src/shared/nsflags.h
index dcac6cd0b2..51bc590621 100644
--- a/src/shared/nsflags.h
+++ b/src/shared/nsflags.h
@@ -42,6 +42,13 @@ unsigned long namespace_flag_from_string(const char *name);
int namespace_flag_from_string_many(const char *name, unsigned long *ret);
int namespace_flag_to_string_many(unsigned long flags, char **ret);
+static inline int namespace_flag_to_string_many_with_check(unsigned long n, char **s) {
+ if ((n & NAMESPACE_FLAGS_ALL) != n)
+ return -EINVAL;
+
+ return namespace_flag_to_string_many(n, s);
+}
+
struct namespace_flag_map {
unsigned long flag;
const char *name;
diff --git a/src/shared/pager.c b/src/shared/pager.c
index 39997278f1..75db3c985b 100644
--- a/src/shared/pager.c
+++ b/src/shared/pager.c
@@ -42,6 +42,11 @@
static pid_t pager_pid = 0;
+static int stored_stdout = -1;
+static int stored_stderr = -1;
+static bool stdout_redirected = false;
+static bool stderr_redirected = false;
+
noreturn static void pager_fallback(void) {
int r;
@@ -54,15 +59,10 @@ noreturn static void pager_fallback(void) {
_exit(EXIT_SUCCESS);
}
-static int stored_stdout = -1;
-static int stored_stderr = -1;
-static bool stdout_redirected = false;
-static bool stderr_redirected = false;
-
int pager_open(bool no_pager, bool jump_to_end) {
_cleanup_close_pair_ int fd[2] = { -1, -1 };
const char *pager;
- pid_t parent_pid;
+ int r;
if (no_pager)
return 0;
@@ -73,6 +73,9 @@ int pager_open(bool no_pager, bool jump_to_end) {
if (terminal_is_dumb())
return 0;
+ if (!is_main_thread())
+ return -EPERM;
+
pager = getenv("SYSTEMD_PAGER");
if (!pager)
pager = getenv("PAGER");
@@ -89,18 +92,13 @@ int pager_open(bool no_pager, bool jump_to_end) {
if (pipe2(fd, O_CLOEXEC) < 0)
return log_error_errno(errno, "Failed to create pager pipe: %m");
- parent_pid = getpid_cached();
-
- pager_pid = fork();
- if (pager_pid < 0)
- return log_error_errno(errno, "Failed to fork pager: %m");
-
- /* In the child start the pager */
- if (pager_pid == 0) {
+ r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pager_pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
const char* less_opts, *less_charset;
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
+ /* In the child start the pager */
(void) dup2(fd[0], STDIN_FILENO);
safe_close_pair(fd);
@@ -124,15 +122,6 @@ int pager_open(bool no_pager, bool jump_to_end) {
setenv("LESSCHARSET", less_charset, 1) < 0)
_exit(EXIT_FAILURE);
- /* Make sure the pager goes away when the parent dies */
- if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
- _exit(EXIT_FAILURE);
-
- /* Check whether our parent died before we were able
- * to set the death signal */
- if (getppid() != parent_pid)
- _exit(EXIT_SUCCESS);
-
if (pager) {
execlp(pager, pager, NULL);
execl("/bin/sh", "sh", "-c", pager, NULL);
@@ -204,7 +193,6 @@ int show_man_page(const char *desc, bool null_stdio) {
pid_t pid;
size_t k;
int r;
- siginfo_t status;
k = strlen(desc);
@@ -222,33 +210,15 @@ int show_man_page(const char *desc, bool null_stdio) {
} else
args[1] = desc;
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
-
- if (pid == 0) {
+ r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG|(null_stdio ? FORK_NULL_STDIO : 0)|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
/* Child */
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- if (null_stdio) {
- r = make_null_stdio();
- if (r < 0) {
- log_error_errno(r, "Failed to kill stdio: %m");
- _exit(EXIT_FAILURE);
- }
- }
-
execvp(args[0], (char**) args);
log_error_errno(errno, "Failed to execute man: %m");
_exit(EXIT_FAILURE);
}
- r = wait_for_terminate(pid, &status);
- if (r < 0)
- return r;
-
- log_debug("Exit code %i status %i", status.si_code, status.si_status);
- return status.si_status;
+ return wait_for_terminate_and_check(NULL, pid, 0);
}
diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c
index 62742858c7..fbb232cd45 100644
--- a/src/shared/seccomp-util.c
+++ b/src/shared/seccomp-util.c
@@ -950,11 +950,70 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, u
return 0;
}
+int seccomp_parse_syscall_filter_internal(
+ bool invert,
+ const char *name,
+ int errno_num,
+ Hashmap *filter,
+ bool whitelist,
+ bool warn,
+ const char *unit,
+ const char *filename,
+ unsigned line) {
+
+ int r;
+
+ assert(name);
+ assert(filter);
+
+ if (name[0] == '@') {
+ const SyscallFilterSet *set;
+ const char *i;
+
+ set = syscall_filter_set_find(name);
+ if (!set) {
+ if (warn) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown system call group, ignoring: %s", name);
+ return 0;
+ } else
+ return -EINVAL;
+ }
+
+ NULSTR_FOREACH(i, set->value) {
+ r = seccomp_parse_syscall_filter_internal(invert, i, errno_num, filter, whitelist, warn, unit, filename, line);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ int id;
+
+ id = seccomp_syscall_resolve_name(name);
+ if (id == __NR_SCMP_ERROR) {
+ if (warn) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse system call, ignoring: %s", name);
+ return 0;
+ } else
+ return -EINVAL;
+ }
+
+ /* If we previously wanted to forbid a syscall and now
+ * we want to allow it, then remove it from the list. */
+ if (!invert == whitelist) {
+ r = hashmap_put(filter, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num));
+ if (r < 0)
+ return warn ? log_oom() : -ENOMEM;
+ } else
+ (void) hashmap_remove(filter, INT_TO_PTR(id + 1));
+ }
+
+ return 0;
+}
+
int seccomp_restrict_namespaces(unsigned long retain) {
uint32_t arch;
int r;
- if (log_get_max_level() >= LOG_DEBUG) {
+ if (DEBUG_LOGGING) {
_cleanup_free_ char *s = NULL;
(void) namespace_flag_to_string_many(retain, &s);
@@ -1386,6 +1445,7 @@ int seccomp_memory_deny_write_execute(void) {
block_syscall = SCMP_SYS(mmap);
break;
+ case SCMP_ARCH_PPC:
case SCMP_ARCH_PPC64:
case SCMP_ARCH_PPC64LE:
filter_syscall = SCMP_SYS(mmap);
@@ -1410,7 +1470,7 @@ int seccomp_memory_deny_write_execute(void) {
/* Please add more definitions here, if you port systemd to other architectures! */
-#if !defined(__i386__) && !defined(__x86_64__) && !defined(__powerpc64__) && !defined(__arm__) && !defined(__aarch64__)
+#if !defined(__i386__) && !defined(__x86_64__) && !defined(__powerpc__) && !defined(__powerpc64__) && !defined(__arm__) && !defined(__aarch64__)
#warning "Consider adding the right mmap() syscall definitions here!"
#endif
}
diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h
index ad2ab7f6b5..0b30cdf388 100644
--- a/src/shared/seccomp-util.h
+++ b/src/shared/seccomp-util.h
@@ -81,6 +81,24 @@ int seccomp_add_syscall_filter_item(scmp_filter_ctx *ctx, const char *name, uint
int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action);
int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action);
+int seccomp_parse_syscall_filter_internal(
+ bool invert, const char *name, int errno_num, Hashmap *filter, bool whitelist,
+ bool warn, const char *unit, const char *filename, unsigned line);
+
+static inline int seccomp_parse_syscall_filter_and_warn(
+ bool invert, const char *name, int errno_num, Hashmap *filter, bool whitelist,
+ const char *unit, const char *filename, unsigned line) {
+ assert(unit);
+ assert(filename);
+
+ return seccomp_parse_syscall_filter_internal(invert, name, errno_num, filter, whitelist, true, unit, filename, line);
+}
+
+static inline int seccomp_parse_syscall_filter(
+ bool invert, const char *name, int errno_num, Hashmap *filter, bool whitelist) {
+ return seccomp_parse_syscall_filter_internal(invert, name, errno_num, filter, whitelist, false, NULL, NULL, 0);
+}
+
int seccomp_restrict_archs(Set *archs);
int seccomp_restrict_namespaces(unsigned long retain);
int seccomp_protect_sysctl(void);
diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c
index 9af5faa3dd..f78167c25c 100644
--- a/src/shared/spawn-ask-password-agent.c
+++ b/src/shared/spawn-ask-password-agent.c
@@ -40,8 +40,12 @@ int ask_password_agent_open(void) {
if (!isatty(STDIN_FILENO))
return 0;
- r = fork_agent(&agent_pid,
+ if (!is_main_thread())
+ return -EPERM;
+
+ r = fork_agent("(sd-askpwagent)",
NULL, 0,
+ &agent_pid,
SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH,
SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL);
if (r < 0)
@@ -56,8 +60,7 @@ void ask_password_agent_close(void) {
return;
/* Inform agent that we are done */
- (void) kill(agent_pid, SIGTERM);
- (void) kill(agent_pid, SIGCONT);
+ (void) kill_and_sigcont(agent_pid, SIGTERM);
(void) wait_for_terminate(agent_pid, NULL);
agent_pid = 0;
}
diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c
index 423069fb0e..3c93c79ceb 100644
--- a/src/shared/spawn-polkit-agent.c
+++ b/src/shared/spawn-polkit-agent.c
@@ -38,9 +38,8 @@
static pid_t agent_pid = 0;
int polkit_agent_open(void) {
- int r;
- int pipe_fd[2];
char notify_fd[DECIMAL_STR_MAX(int) + 1];
+ int pipe_fd[2], r;
if (agent_pid > 0)
return 0;
@@ -49,18 +48,21 @@ int polkit_agent_open(void) {
if (geteuid() == 0)
return 0;
- /* We check STDIN here, not STDOUT, since this is about input,
- * not output */
+ /* We check STDIN here, not STDOUT, since this is about input, not output */
if (!isatty(STDIN_FILENO))
return 0;
+ if (!is_main_thread())
+ return -EPERM;
+
if (pipe2(pipe_fd, 0) < 0)
return -errno;
xsprintf(notify_fd, "%i", pipe_fd[1]);
- r = fork_agent(&agent_pid,
+ r = fork_agent("(polkit-agent)",
&pipe_fd[1], 1,
+ &agent_pid,
POLKIT_AGENT_BINARY_PATH,
POLKIT_AGENT_BINARY_PATH, "--notify-fd", notify_fd, "--fallback", NULL);
@@ -84,9 +86,7 @@ void polkit_agent_close(void) {
return;
/* Inform agent that we are done */
- (void) kill(agent_pid, SIGTERM);
- (void) kill(agent_pid, SIGCONT);
-
+ (void) kill_and_sigcont(agent_pid, SIGTERM);
(void) wait_for_terminate(agent_pid, NULL);
agent_pid = 0;
}
diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c
index 189580e3ed..0bc81aaa56 100644
--- a/src/shared/sysctl-util.c
+++ b/src/shared/sysctl-util.c
@@ -18,9 +18,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
#include <string.h>
+#include <unistd.h>
+#include "fd-util.h"
#include "fileio.h"
#include "log.h"
#include "macro.h"
@@ -53,6 +57,7 @@ char *sysctl_normalize(char *s) {
int sysctl_write(const char *property, const char *value) {
char *p;
+ _cleanup_close_ int fd = -1;
assert(property);
assert(value);
@@ -60,7 +65,17 @@ int sysctl_write(const char *property, const char *value) {
log_debug("Setting '%s' to '%s'", property, value);
p = strjoina("/proc/sys/", property);
- return write_string_file(p, value, WRITE_STRING_FILE_DISABLE_BUFFER);
+ fd = open(p, O_WRONLY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (!endswith(value, "\n"))
+ value = strjoina(value, "\n");
+
+ if (write(fd, value, strlen(value)) < 0)
+ return -errno;
+
+ return 0;
}
int sysctl_read(const char *property, char **content) {
diff --git a/src/shared/test-tables.h b/src/shared/test-tables.h
index 6b223b1ee5..dd8bf0b582 100644
--- a/src/shared/test-tables.h
+++ b/src/shared/test-tables.h
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
typedef const char* (*lookup_t)(int);
typedef int (*reverse_t)(const char*);
diff --git a/src/shared/tests.c b/src/shared/tests.c
index d78ab7b069..d4781acabf 100644
--- a/src/shared/tests.c
+++ b/src/shared/tests.c
@@ -48,7 +48,7 @@ const char* get_testdata_dir(const char *suffix) {
if (env) {
if (access(env, F_OK) < 0) {
fputs("ERROR: $SYSTEMD_TEST_DATA directory does not exist\n", stderr);
- exit(1);
+ exit(EXIT_FAILURE);
}
strncpy(testdir, env, sizeof(testdir) - 1);
} else {
@@ -65,7 +65,7 @@ const char* get_testdata_dir(const char *suffix) {
/* test this without the suffix, as it may contain a glob */
if (access(testdir, F_OK) < 0) {
fputs("ERROR: Cannot find testdata directory, set $SYSTEMD_TEST_DATA\n", stderr);
- exit(1);
+ exit(EXIT_FAILURE);
}
}
diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c
index 1715c0fb24..cab1cd6a2d 100644
--- a/src/shared/utmp-wtmp.c
+++ b/src/shared/utmp-wtmp.c
@@ -330,7 +330,7 @@ static int write_to_terminal(const char *tty, const char *message) {
assert(tty);
assert(message);
- fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC);
+ fd = open(tty, O_WRONLY|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
if (fd < 0 || !isatty(fd))
return -errno;
diff --git a/src/shared/volatile-util.c b/src/shared/volatile-util.c
index 85512d00af..c92ad0adc0 100644
--- a/src/shared/volatile-util.c
+++ b/src/shared/volatile-util.c
@@ -18,6 +18,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+
#include "alloc-util.h"
#include "macro.h"
#include "parse-util.h"
diff --git a/src/shared/wireguard-netlink.h b/src/shared/wireguard-netlink.h
new file mode 100644
index 0000000000..eb170915a6
--- /dev/null
+++ b/src/shared/wireguard-netlink.h
@@ -0,0 +1,179 @@
+#pragma once
+
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR MIT)
+ *
+ * Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * Documentation
+ * =============
+ *
+ * The below enums and macros are for interfacing with WireGuard, using generic
+ * netlink, with family WG_GENL_NAME and version WG_GENL_VERSION. It defines two
+ * methods: get and set. Note that while they share many common attributes, these
+ * two functions actually accept a slightly different set of inputs and outputs.
+ *
+ * WG_CMD_GET_DEVICE
+ * -----------------
+ *
+ * May only be called via NLM_F_REQUEST | NLM_F_DUMP. The command should contain
+ * one but not both of:
+ *
+ * WGDEVICE_A_IFINDEX: NLA_U32
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
+ *
+ * The kernel will then return several messages (NLM_F_MULTI) containing the following
+ * tree of nested items:
+ *
+ * WGDEVICE_A_IFINDEX: NLA_U32
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
+ * WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN
+ * WGDEVICE_A_PUBLIC_KEY: len WG_KEY_LEN
+ * WGDEVICE_A_LISTEN_PORT: NLA_U16
+ * WGDEVICE_A_FWMARK: NLA_U32
+ * WGDEVICE_A_PEERS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN
+ * WGPEER_A_PRESHARED_KEY: len WG_KEY_LEN
+ * WGPEER_A_ENDPOINT: struct sockaddr_in or struct sockaddr_in6
+ * WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16
+ * WGPEER_A_LAST_HANDSHAKE_TIME: struct timespec
+ * WGPEER_A_RX_BYTES: NLA_U64
+ * WGPEER_A_TX_BYTES: NLA_U64
+ * WGPEER_A_ALLOWEDIPS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGALLOWEDIP_A_FAMILY: NLA_U16
+ * WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr
+ * WGALLOWEDIP_A_CIDR_MASK: NLA_U8
+ * 1: NLA_NESTED
+ * ...
+ * 2: NLA_NESTED
+ * ...
+ * ...
+ * 1: NLA_NESTED
+ * ...
+ * ...
+ *
+ * It is possible that all of the allowed IPs of a single peer will not
+ * fit within a single netlink message. In that case, the same peer will
+ * be written in the following message, except it will only contain
+ * WGPEER_A_PUBLIC_KEY and WGPEER_A_ALLOWEDIPS. This may occur several
+ * times in a row for the same peer. It is then up to the receiver to
+ * coalesce adjacent peers. Likewise, it is possible that all peers will
+ * not fit within a single message. So, subsequent peers will be sent
+ * in following messages, except those will only contain WGDEVICE_A_IFNAME
+ * and WGDEVICE_A_PEERS. It is then up to the receiver to coalesce these
+ * messages to form the complete list of peers.
+ *
+ * Since this is an NLA_F_DUMP command, the final message will always be
+ * NLMSG_DONE, even if an error occurs. However, this NLMSG_DONE message
+ * contains an integer error code. It is either zero or a negative error
+ * code corresponding to the errno.
+ *
+ * WG_CMD_SET_DEVICE
+ * -----------------
+ *
+ * May only be called via NLM_F_REQUEST. The command should contain the following
+ * tree of nested items, containing one but not both of WGDEVICE_A_IFINDEX
+ * and WGDEVICE_A_IFNAME:
+ *
+ * WGDEVICE_A_IFINDEX: NLA_U32
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
+ * WGDEVICE_A_FLAGS: NLA_U32, 0 or WGDEVICE_F_REPLACE_PEERS if all current
+ * peers should be removed prior to adding the list below.
+ * WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove
+ * WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly
+ * WGDEVICE_A_FWMARK: NLA_U32, 0 to disable
+ * WGDEVICE_A_PEERS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN
+ * WGPEER_A_FLAGS: NLA_U32, 0 and/or WGPEER_F_REMOVE_ME if the specified peer
+ * should be removed rather than added/updated and/or
+ * WGPEER_F_REPLACE_ALLOWEDIPS if all current allowed IPs of
+ * this peer should be removed prior to adding the list below.
+ * WGPEER_A_PRESHARED_KEY: len WG_KEY_LEN, all zeros to remove
+ * WGPEER_A_ENDPOINT: struct sockaddr_in or struct sockaddr_in6
+ * WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16, 0 to disable
+ * WGPEER_A_ALLOWEDIPS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGALLOWEDIP_A_FAMILY: NLA_U16
+ * WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr
+ * WGALLOWEDIP_A_CIDR_MASK: NLA_U8
+ * 1: NLA_NESTED
+ * ...
+ * 2: NLA_NESTED
+ * ...
+ * ...
+ * 1: NLA_NESTED
+ * ...
+ * ...
+ *
+ * It is possible that the amount of configuration data exceeds that of
+ * the maximum message length accepted by the kernel. In that case,
+ * several messages should be sent one after another, with each
+ * successive one filling in information not contained in the prior. Note
+ * that if WGDEVICE_F_REPLACE_PEERS is specified in the first message, it
+ * probably should not be specified in fragments that come after, so that
+ * the list of peers is only cleared the first time but appened after.
+ * Likewise for peers, if WGPEER_F_REPLACE_ALLOWEDIPS is specified in the
+ * first message of a peer, it likely should not be specified in subsequent
+ * fragments.
+ *
+ * If an error occurs, NLMSG_ERROR will reply containing an errno.
+ */
+
+#define WG_GENL_NAME "wireguard"
+#define WG_GENL_VERSION 1
+
+#define WG_KEY_LEN 32
+
+enum wg_cmd {
+ WG_CMD_GET_DEVICE,
+ WG_CMD_SET_DEVICE,
+ __WG_CMD_MAX
+};
+#define WG_CMD_MAX (__WG_CMD_MAX - 1)
+
+enum wgdevice_flag {
+ WGDEVICE_F_REPLACE_PEERS = 1U << 0
+};
+enum wgdevice_attribute {
+ WGDEVICE_A_UNSPEC,
+ WGDEVICE_A_IFINDEX,
+ WGDEVICE_A_IFNAME,
+ WGDEVICE_A_PRIVATE_KEY,
+ WGDEVICE_A_PUBLIC_KEY,
+ WGDEVICE_A_FLAGS,
+ WGDEVICE_A_LISTEN_PORT,
+ WGDEVICE_A_FWMARK,
+ WGDEVICE_A_PEERS,
+ __WGDEVICE_A_LAST
+};
+#define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1)
+
+enum wgpeer_flag {
+ WGPEER_F_REMOVE_ME = 1U << 0,
+ WGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1
+};
+enum wgpeer_attribute {
+ WGPEER_A_UNSPEC,
+ WGPEER_A_PUBLIC_KEY,
+ WGPEER_A_PRESHARED_KEY,
+ WGPEER_A_FLAGS,
+ WGPEER_A_ENDPOINT,
+ WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
+ WGPEER_A_LAST_HANDSHAKE_TIME,
+ WGPEER_A_RX_BYTES,
+ WGPEER_A_TX_BYTES,
+ WGPEER_A_ALLOWEDIPS,
+ __WGPEER_A_LAST
+};
+#define WGPEER_A_MAX (__WGPEER_A_LAST - 1)
+
+enum wgallowedip_attribute {
+ WGALLOWEDIP_A_UNSPEC,
+ WGALLOWEDIP_A_FAMILY,
+ WGALLOWEDIP_A_IPADDR,
+ WGALLOWEDIP_A_CIDR_MASK,
+ __WGALLOWEDIP_A_LAST
+};
+#define WGALLOWEDIP_A_MAX (__WGALLOWEDIP_A_LAST - 1)
diff --git a/src/sulogin-shell/sulogin-shell.c b/src/sulogin-shell/sulogin-shell.c
index 70659df417..33dc07c5bd 100644
--- a/src/sulogin-shell/sulogin-shell.c
+++ b/src/sulogin-shell/sulogin-shell.c
@@ -81,24 +81,19 @@ static int start_default_target(sd_bus *bus) {
static int fork_wait(const char* const cmdline[]) {
pid_t pid;
+ int r;
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "fork(): %m");
- if (pid == 0) {
-
+ r = safe_fork("(sulogin)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
/* Child */
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
execv(cmdline[0], (char**) cmdline);
log_error_errno(errno, "Failed to execute %s: %m", cmdline[0]);
_exit(EXIT_FAILURE); /* Operational error */
}
- return wait_for_terminate_and_warn(cmdline[0], pid, false);
+ return wait_for_terminate_and_check(cmdline[0], pid, WAIT_LOG_ABNORMAL);
}
static void print_mode(const char* mode) {
diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c
index 1796bae340..ec05e7b9e6 100644
--- a/src/system-update-generator/system-update-generator.c
+++ b/src/system-update-generator/system-update-generator.c
@@ -79,7 +79,8 @@ int main(int argc, char *argv[]) {
if (argc > 1)
arg_dest = argv[2];
- log_set_target(LOG_TARGET_SAFE);
+ log_set_prohibit_ipc(true);
+ log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 18c64241ba..5732d88a17 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -84,6 +84,7 @@
#include "stat-util.h"
#include "strv.h"
#include "terminal-util.h"
+#include "unit-def.h"
#include "unit-name.h"
#include "user-util.h"
#include "util.h"
@@ -332,7 +333,7 @@ static bool install_client_side(void) {
/* Decides when to execute enable/disable/... operations
* client-side rather than server-side. */
- if (running_in_chroot() > 0)
+ if (running_in_chroot_or_offline())
return true;
if (sd_booted() <= 0)
@@ -2649,55 +2650,32 @@ static int unit_find_paths(
static int get_state_one_unit(sd_bus *bus, const char *name, UnitActiveState *active_state) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ char *buf = NULL;
+ _cleanup_free_ char *buf = NULL, *path = NULL;
UnitActiveState state;
- const char *path;
int r;
assert(name);
assert(active_state);
- /* We don't use unit_dbus_path_from_name() directly since we don't want to load the unit unnecessarily, if it
- * isn't loaded. */
- r = sd_bus_call_method(
+ path = unit_dbus_path_from_name(name);
+ if (!path)
+ return log_oom();
+
+ r = sd_bus_get_property_string(
bus,
"org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GetUnit",
+ path,
+ "org.freedesktop.systemd1.Unit",
+ "ActiveState",
&error,
- &reply,
- "s", name);
- if (r < 0) {
- if (!sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT))
- return log_error_errno(r, "Failed to retrieve unit: %s", bus_error_message(&error, r));
-
- /* The unit is currently not loaded, hence say it's "inactive", since all units that aren't loaded are
- * considered inactive. */
- state = UNIT_INACTIVE;
-
- } else {
- r = sd_bus_message_read(reply, "o", &path);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_get_property_string(
- bus,
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.systemd1.Unit",
- "ActiveState",
- &error,
- &buf);
- if (r < 0)
- return log_error_errno(r, "Failed to retrieve unit state: %s", bus_error_message(&error, r));
+ &buf);
+ if (r < 0)
+ return log_error_errno(r, "Failed to retrieve unit state: %s", bus_error_message(&error, r));
- state = unit_active_state_from_string(buf);
- if (state == _UNIT_ACTIVE_STATE_INVALID) {
- log_error("Invalid unit state '%s' for: %s", buf, name);
- return -EINVAL;
- }
+ state = unit_active_state_from_string(buf);
+ if (state == _UNIT_ACTIVE_STATE_INVALID) {
+ log_error("Invalid unit state '%s' for: %s", buf, name);
+ return -EINVAL;
}
*active_state = state;
@@ -2904,7 +2882,6 @@ static int start_unit_one(
if (wait_context) {
_cleanup_free_ char *unit_path = NULL;
- const char* mt;
log_debug("Watching for property changes of %s", name);
r = sd_bus_call_method(
@@ -2927,13 +2904,15 @@ static int start_unit_one(
if (r < 0)
return log_error_errno(r, "Failed to add unit path %s to set: %m", unit_path);
- mt = strjoina("type='signal',"
- "interface='org.freedesktop.DBus.Properties',"
- "path='", unit_path, "',"
- "member='PropertiesChanged'");
- r = sd_bus_add_match(bus, &wait_context->match, mt, on_properties_changed, wait_context);
+ r = sd_bus_match_signal_async(bus,
+ &wait_context->match,
+ NULL,
+ unit_path,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ on_properties_changed, NULL, wait_context);
if (r < 0)
- return log_error_errno(r, "Failed to add match for PropertiesChanged signal: %m");
+ return log_error_errno(r, "Failed to request match for PropertiesChanged signal: %m");
}
log_debug("%s manager for %s on %s, %s",
@@ -3149,22 +3128,21 @@ static int start_unit(int argc, char *argv[], void *userdata) {
}
if (arg_wait) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-
wait_context.unit_paths = set_new(&string_hash_ops);
if (!wait_context.unit_paths)
return log_oom();
- r = sd_bus_call_method(
+ r = sd_bus_call_method_async(
bus,
+ NULL,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"Subscribe",
- &error,
- NULL, NULL);
+ NULL, NULL,
+ NULL);
if (r < 0)
- return log_error_errno(r, "Failed to enable subscription: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to enable subscription: %m");
r = sd_event_default(&wait_context.event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
@@ -3538,10 +3516,10 @@ static int load_kexec_kernel(void) {
if (arg_dry_run)
return 0;
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
- else if (pid == 0) {
+ r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
const char* const args[] = {
KEXEC,
@@ -3551,15 +3529,11 @@ static int load_kexec_kernel(void) {
NULL };
/* Child */
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
execv(args[0], (char * const *) args);
_exit(EXIT_FAILURE);
- } else
- return wait_for_terminate_and_warn("kexec", pid, true);
+ }
+
+ return wait_for_terminate_and_check("kexec", pid, WAIT_LOG);
}
static int set_exit_code(uint8_t code) {
@@ -4044,7 +4018,8 @@ static void print_status_info(
if (i->load_error != 0)
printf(" Loaded: %s%s%s (Reason: %s)\n",
on, strna(i->load_state), off, i->load_error);
- else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset))
+ else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset) &&
+ !STR_IN_SET(i->unit_file_state, "generated", "transient"))
printf(" Loaded: %s%s%s (%s; %s; vendor preset: %s)\n",
on, strna(i->load_state), off, path, i->unit_file_state, i->unit_file_preset);
else if (path && !isempty(i->unit_file_state))
@@ -5582,6 +5557,7 @@ static int set_property(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *n = NULL;
+ UnitType t;
sd_bus *bus;
int r;
@@ -5605,6 +5581,12 @@ static int set_property(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
+ t = unit_name_to_type(n);
+ if (t < 0) {
+ log_error("Invalid unit type: %s", n);
+ return -EINVAL;
+ }
+
r = sd_bus_message_append(m, "sb", n, arg_runtime);
if (r < 0)
return bus_log_create_error(r);
@@ -5613,7 +5595,7 @@ static int set_property(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_create_error(r);
- r = bus_append_unit_property_assignment_many(m, strv_skip(argv, 2));
+ r = bus_append_unit_property_assignment_many(m, t, strv_skip(argv, 2));
if (r < 0)
return r;
@@ -6060,7 +6042,6 @@ static int enable_sysv_units(const char *verb, char **args) {
_cleanup_free_ char *p = NULL, *q = NULL, *l = NULL;
bool found_native = false, found_sysv;
- siginfo_t status;
const char *name;
unsigned c = 1;
pid_t pid;
@@ -6114,41 +6095,31 @@ static int enable_sysv_units(const char *verb, char **args) {
if (!arg_quiet)
log_info("Executing: %s", l);
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
- else if (pid == 0) {
+ j = safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (j < 0)
+ return j;
+ if (j == 0) {
/* Child */
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
execv(argv[0], (char**) argv);
log_error_errno(errno, "Failed to execute %s: %m", argv[0]);
_exit(EXIT_FAILURE);
}
- j = wait_for_terminate(pid, &status);
+ j = wait_for_terminate_and_check("sysv-install", pid, WAIT_LOG_ABNORMAL);
if (j < 0)
- return log_error_errno(j, "Failed to wait for child: %m");
-
- if (status.si_code == CLD_EXITED) {
- if (streq(verb, "is-enabled")) {
- if (status.si_status == 0) {
- if (!arg_quiet)
- puts("enabled");
- r = 1;
- } else {
- if (!arg_quiet)
- puts("disabled");
- }
+ return j;
+ if (streq(verb, "is-enabled")) {
+ if (j == EXIT_SUCCESS) {
+ if (!arg_quiet)
+ puts("enabled");
+ r = 1;
+ } else {
+ if (!arg_quiet)
+ puts("disabled");
+ }
- } else if (status.si_status != 0)
- return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */
- } else {
- log_error("Unexpected waitid() result.");
- return -EPROTO;
- }
+ } else if (j != EXIT_SUCCESS)
+ return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */
if (found_native)
continue;
@@ -6253,7 +6224,6 @@ static int normalize_names(char **names, bool warn_if_path) {
}
static int unit_exists(LookupPaths *lp, const char *unit) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *path = NULL;
static const struct bus_properties_map property_map[] = {
@@ -6276,22 +6246,10 @@ static int unit_exists(LookupPaths *lp, const char *unit) {
if (r < 0)
return r;
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.DBus.Properties",
- "GetAll",
- &error,
- &reply,
- "s", "");
+ r = bus_map_all_properties(bus, "org.freedesktop.systemd1", path, property_map, &error, &info);
if (r < 0)
return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r));
- r = bus_message_map_all_properties(reply, property_map, &error, &info);
- if (r < 0)
- return log_error_errno(r, "Failed to map properties: %s", bus_error_message(&error, r));
-
return !streq_ptr(info.load_state, "not-found") || !streq_ptr(info.active_state, "inactive");
}
@@ -6986,25 +6944,20 @@ static int unit_file_create_copy(
}
static int run_editor(char **paths) {
- pid_t pid;
int r;
assert(paths);
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
-
- if (pid == 0) {
+ r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0) {
const char **args;
char *editor, **editor_args = NULL;
char **tmp_path, **original_path, *p;
unsigned n_editor_args = 0, i = 1;
size_t argc;
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
argc = strv_length(paths)/2 + 1;
/* SYSTEMD_EDITOR takes precedence over EDITOR which takes precedence over VISUAL
@@ -7060,10 +7013,6 @@ static int run_editor(char **paths) {
_exit(EXIT_FAILURE);
}
- r = wait_for_terminate_and_warn("editor", pid, true);
- if (r < 0)
- return log_error_errno(r, "Failed to wait for child: %m");
-
return 0;
}
@@ -8380,7 +8329,7 @@ static int talk_initctl(void) {
request.runlevel = rl;
- fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY);
+ fd = open(INIT_FIFO, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
if (errno == ENOENT)
return 0;
@@ -8401,72 +8350,72 @@ static int talk_initctl(void) {
static int systemctl_main(int argc, char *argv[]) {
static const Verb verbs[] = {
- { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_NOCHROOT, list_units },
- { "list-unit-files", VERB_ANY, VERB_ANY, 0, list_unit_files },
- { "list-sockets", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_sockets },
- { "list-timers", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_timers },
- { "list-jobs", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_jobs },
- { "list-machines", VERB_ANY, VERB_ANY, VERB_NOCHROOT|VERB_MUSTBEROOT, list_machines },
- { "clear-jobs", VERB_ANY, 1, VERB_NOCHROOT, trivial_method },
- { "cancel", VERB_ANY, VERB_ANY, VERB_NOCHROOT, cancel_job },
- { "start", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
- { "stop", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
- { "condstop", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with ALTLinux */
- { "reload", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
- { "restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
- { "try-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
- { "reload-or-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
- { "reload-or-try-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatbility with old systemctl <= 228 */
- { "try-reload-or-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
- { "force-reload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with SysV */
- { "condreload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with ALTLinux */
- { "condrestart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with RH */
- { "isolate", 2, 2, VERB_NOCHROOT, start_unit },
- { "kill", 2, VERB_ANY, VERB_NOCHROOT, kill_unit },
- { "is-active", 2, VERB_ANY, VERB_NOCHROOT, check_unit_active },
- { "check", 2, VERB_ANY, VERB_NOCHROOT, check_unit_active },
- { "is-failed", 2, VERB_ANY, VERB_NOCHROOT, check_unit_failed },
- { "show", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show },
- { "cat", 2, VERB_ANY, VERB_NOCHROOT, cat },
- { "status", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show },
- { "help", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show },
- { "daemon-reload", VERB_ANY, 1, VERB_NOCHROOT, daemon_reload },
- { "daemon-reexec", VERB_ANY, 1, VERB_NOCHROOT, daemon_reload },
- { "show-environment", VERB_ANY, 1, VERB_NOCHROOT, show_environment },
- { "set-environment", 2, VERB_ANY, VERB_NOCHROOT, set_environment },
- { "unset-environment", 2, VERB_ANY, VERB_NOCHROOT, set_environment },
- { "import-environment", VERB_ANY, VERB_ANY, VERB_NOCHROOT, import_environment },
- { "halt", VERB_ANY, 1, VERB_NOCHROOT, start_system_special },
- { "poweroff", VERB_ANY, 1, VERB_NOCHROOT, start_system_special },
- { "reboot", VERB_ANY, 2, VERB_NOCHROOT, start_system_special },
- { "kexec", VERB_ANY, 1, VERB_NOCHROOT, start_system_special },
- { "suspend", VERB_ANY, 1, VERB_NOCHROOT, start_system_special },
- { "hibernate", VERB_ANY, 1, VERB_NOCHROOT, start_system_special },
- { "hybrid-sleep", VERB_ANY, 1, VERB_NOCHROOT, start_system_special },
- { "default", VERB_ANY, 1, VERB_NOCHROOT, start_special },
- { "rescue", VERB_ANY, 1, VERB_NOCHROOT, start_system_special },
- { "emergency", VERB_ANY, 1, VERB_NOCHROOT, start_system_special },
- { "exit", VERB_ANY, 2, VERB_NOCHROOT, start_special },
- { "reset-failed", VERB_ANY, VERB_ANY, VERB_NOCHROOT, reset_failed },
- { "enable", 2, VERB_ANY, 0, enable_unit },
- { "disable", 2, VERB_ANY, 0, enable_unit },
- { "is-enabled", 2, VERB_ANY, 0, unit_is_enabled },
- { "reenable", 2, VERB_ANY, 0, enable_unit },
- { "preset", 2, VERB_ANY, 0, enable_unit },
- { "preset-all", VERB_ANY, 1, 0, preset_all },
- { "mask", 2, VERB_ANY, 0, enable_unit },
- { "unmask", 2, VERB_ANY, 0, enable_unit },
- { "link", 2, VERB_ANY, 0, enable_unit },
- { "revert", 2, VERB_ANY, 0, enable_unit },
- { "switch-root", 2, VERB_ANY, VERB_NOCHROOT, switch_root },
- { "list-dependencies", VERB_ANY, 2, VERB_NOCHROOT, list_dependencies },
- { "set-default", 2, 2, 0, set_default },
- { "get-default", VERB_ANY, 1, 0, get_default },
- { "set-property", 3, VERB_ANY, VERB_NOCHROOT, set_property },
- { "is-system-running", VERB_ANY, 1, 0, is_system_running },
- { "add-wants", 3, VERB_ANY, 0, add_dependency },
- { "add-requires", 3, VERB_ANY, 0, add_dependency },
- { "edit", 2, VERB_ANY, VERB_NOCHROOT, edit },
+ { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, list_units },
+ { "list-unit-files", VERB_ANY, VERB_ANY, 0, list_unit_files },
+ { "list-sockets", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_sockets },
+ { "list-timers", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_timers },
+ { "list-jobs", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_jobs },
+ { "list-machines", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY|VERB_MUST_BE_ROOT, list_machines },
+ { "clear-jobs", VERB_ANY, 1, VERB_ONLINE_ONLY, trivial_method },
+ { "cancel", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, cancel_job },
+ { "start", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
+ { "stop", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
+ { "condstop", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with ALTLinux */
+ { "reload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
+ { "restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
+ { "try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
+ { "reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
+ { "reload-or-try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatbility with old systemctl <= 228 */
+ { "try-reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
+ { "force-reload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with SysV */
+ { "condreload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with ALTLinux */
+ { "condrestart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with RH */
+ { "isolate", 2, 2, VERB_ONLINE_ONLY, start_unit },
+ { "kill", 2, VERB_ANY, VERB_ONLINE_ONLY, kill_unit },
+ { "is-active", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active },
+ { "check", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active }, /* deprecated alias of is-active */
+ { "is-failed", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_failed },
+ { "show", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, show },
+ { "cat", 2, VERB_ANY, VERB_ONLINE_ONLY, cat },
+ { "status", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, show },
+ { "help", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, show },
+ { "daemon-reload", VERB_ANY, 1, VERB_ONLINE_ONLY, daemon_reload },
+ { "daemon-reexec", VERB_ANY, 1, VERB_ONLINE_ONLY, daemon_reload },
+ { "show-environment", VERB_ANY, 1, VERB_ONLINE_ONLY, show_environment },
+ { "set-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, set_environment },
+ { "unset-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, set_environment },
+ { "import-environment", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, import_environment },
+ { "halt", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
+ { "poweroff", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
+ { "reboot", VERB_ANY, 2, VERB_ONLINE_ONLY, start_system_special },
+ { "kexec", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
+ { "suspend", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
+ { "hibernate", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
+ { "hybrid-sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
+ { "default", VERB_ANY, 1, VERB_ONLINE_ONLY, start_special },
+ { "rescue", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
+ { "emergency", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special },
+ { "exit", VERB_ANY, 2, VERB_ONLINE_ONLY, start_special },
+ { "reset-failed", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, reset_failed },
+ { "enable", 2, VERB_ANY, 0, enable_unit },
+ { "disable", 2, VERB_ANY, 0, enable_unit },
+ { "is-enabled", 2, VERB_ANY, 0, unit_is_enabled },
+ { "reenable", 2, VERB_ANY, 0, enable_unit },
+ { "preset", 2, VERB_ANY, 0, enable_unit },
+ { "preset-all", VERB_ANY, 1, 0, preset_all },
+ { "mask", 2, VERB_ANY, 0, enable_unit },
+ { "unmask", 2, VERB_ANY, 0, enable_unit },
+ { "link", 2, VERB_ANY, 0, enable_unit },
+ { "revert", 2, VERB_ANY, 0, enable_unit },
+ { "switch-root", 2, VERB_ANY, VERB_ONLINE_ONLY, switch_root },
+ { "list-dependencies", VERB_ANY, 2, VERB_ONLINE_ONLY, list_dependencies },
+ { "set-default", 2, 2, 0, set_default },
+ { "get-default", VERB_ANY, 1, 0, get_default },
+ { "set-property", 3, VERB_ANY, VERB_ONLINE_ONLY, set_property },
+ { "is-system-running", VERB_ANY, 1, 0, is_system_running },
+ { "add-wants", 3, VERB_ANY, 0, add_dependency },
+ { "add-requires", 3, VERB_ANY, 0, add_dependency },
+ { "edit", 2, VERB_ANY, VERB_ONLINE_ONLY, edit },
{}
};
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index c5c7096d55..82e7d445d2 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -33,6 +33,10 @@
_SD_BEGIN_DECLARATIONS;
+#define SD_BUS_DEFAULT ((sd_bus *) 1)
+#define SD_BUS_DEFAULT_USER ((sd_bus *) 2)
+#define SD_BUS_DEFAULT_SYSTEM ((sd_bus *) 3)
+
/* Types */
typedef struct sd_bus sd_bus;
@@ -150,8 +154,14 @@ 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_set_exit_on_disconnect(sd_bus *bus, int b);
int sd_bus_get_exit_on_disconnect(sd_bus *bus);
+int sd_bus_set_watch_bind(sd_bus *bus, int b);
+int sd_bus_get_watch_bind(sd_bus *bus);
+int sd_bus_set_connected_signal(sd_bus *bus, int b);
+int sd_bus_get_connected_signal(sd_bus *bus);
+int sd_bus_set_sender(sd_bus *bus, const char *sender);
+int sd_bus_get_sender(sd_bus *bus, const char **ret);
-int sd_bus_start(sd_bus *ret);
+int sd_bus_start(sd_bus *bus);
int sd_bus_try_close(sd_bus *bus);
void sd_bus_close(sd_bus *bus);
@@ -163,6 +173,7 @@ 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_is_ready(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);
@@ -193,6 +204,7 @@ 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_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_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);
@@ -267,6 +279,7 @@ 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_sender(sd_bus_message *m, const char *sender);
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, ...);
@@ -300,7 +313,9 @@ int sd_bus_message_rewind(sd_bus_message *m, int complete);
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_request_name_async(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, uint64_t flags, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_release_name(sd_bus *bus, const char *name);
+int sd_bus_release_name_async(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, sd_bus_message_handler_t callback, void *userdata);
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);
@@ -336,6 +351,9 @@ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *in
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);
+int sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata);
+int sd_bus_match_signal_async(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t match_callback, sd_bus_message_handler_t add_callback, void *userdata);
+
/* Credential handling */
int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t creds_mask);
diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h
index 37803c71d8..cadb32a051 100644
--- a/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/sd-dhcp6-client.h
@@ -23,6 +23,7 @@
#include <inttypes.h>
#include <net/ethernet.h>
+#include <stdbool.h>
#include <sys/types.h>
#include "sd-dhcp6-lease.h"
@@ -64,6 +65,8 @@ enum {
SD_DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */
SD_DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */
+ SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, prefix delegation */
+ SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, prefix delegation */
SD_DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */
@@ -116,6 +119,8 @@ int sd_dhcp6_client_get_information_request(
int sd_dhcp6_client_set_request_option(
sd_dhcp6_client *client,
uint16_t option);
+int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
+ bool delegation);
int sd_dhcp6_client_get_lease(
sd_dhcp6_client *client,
diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h
index 5807b1836b..22a5f8ce75 100644
--- a/src/systemd/sd-dhcp6-lease.h
+++ b/src/systemd/sd-dhcp6-lease.h
@@ -36,6 +36,11 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid);
+void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease);
+int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
+ uint8_t *prefix_len,
+ 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);
diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h
index 9083d5fa9e..ec4b7bcf69 100644
--- a/src/systemd/sd-event.h
+++ b/src/systemd/sd-event.h
@@ -26,6 +26,7 @@
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include <sys/types.h>
+#include <time.h>
#include "_sd-common.h"
@@ -40,6 +41,8 @@
_SD_BEGIN_DECLARATIONS;
+#define SD_EVENT_DEFAULT ((sd_event *) 1)
+
typedef struct sd_event sd_event;
typedef struct sd_event_source sd_event_source;
@@ -124,6 +127,8 @@ 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_fd_own(sd_event_source *s);
+int sd_event_source_set_io_fd_own(sd_event_source *s, int own);
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);
diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h
index d6e3816c64..e742807e92 100644
--- a/src/systemd/sd-netlink.h
+++ b/src/systemd/sd-netlink.h
@@ -34,7 +34,9 @@
_SD_BEGIN_DECLARATIONS;
typedef struct sd_netlink sd_netlink;
+typedef struct sd_genl_socket sd_genl_socket;
typedef struct sd_netlink_message sd_netlink_message;
+typedef enum {SD_GENL_ID_CTRL, SD_GENL_WIREGUARD} sd_genl_family;
/* callback */
@@ -94,6 +96,9 @@ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type,
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_open_array(sd_netlink_message *m, uint16_t type);
+int sd_netlink_message_cancel_array(sd_netlink_message *m);
+
int sd_netlink_message_rewind(sd_netlink_message *m);
sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m);
@@ -177,6 +182,10 @@ int sd_rtnl_message_routing_policy_rule_get_rtm_type(sd_netlink_message *m, unsi
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink, sd_netlink_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink_message, sd_netlink_message_unref);
+/* genl */
+int sd_genl_socket_open(sd_netlink **nl);
+int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **m);
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h
index 94d5e71e8a..e319a82dbf 100644
--- a/src/systemd/sd-radv.h
+++ b/src/systemd/sd-radv.h
@@ -24,6 +24,7 @@
#include <inttypes.h>
#include <net/ethernet.h>
#include <netinet/in.h>
+#include <stdbool.h>
#include <sys/types.h>
#include "sd-ndisc.h"
@@ -62,7 +63,9 @@ int sd_radv_set_router_lifetime(sd_radv *ra, uint32_t router_lifetime);
int sd_radv_set_managed_information(sd_radv *ra, int managed);
int sd_radv_set_other_information(sd_radv *ra, int other);
int sd_radv_set_preference(sd_radv *ra, unsigned preference);
-int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p);
+int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, bool dynamic);
+sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, struct in6_addr *prefix,
+ uint8_t prefixlen);
int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
const struct in6_addr *dns, size_t n_dns);
int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime, char **search_list);
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index d8009458ee..e06b4b6d5b 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -64,6 +64,7 @@ typedef struct Item {
uid_t uid;
bool gid_set:1;
+ bool gid_must_exist:1;
bool uid_set:1;
bool todo_user:1;
@@ -74,9 +75,9 @@ static char *arg_root = NULL;
static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysusers.d");
-static Hashmap *users = NULL, *groups = NULL;
-static Hashmap *todo_uids = NULL, *todo_gids = NULL;
-static Hashmap *members = NULL;
+static OrderedHashmap *users = NULL, *groups = NULL;
+static OrderedHashmap *todo_uids = NULL, *todo_gids = NULL;
+static OrderedHashmap *members = NULL;
static Hashmap *database_uid = NULL, *database_user = NULL;
static Hashmap *database_gid = NULL, *database_group = NULL;
@@ -256,7 +257,7 @@ static int putgrent_with_members(const struct group *gr, FILE *group) {
assert(gr);
assert(group);
- a = hashmap_get(members, gr->gr_name);
+ a = ordered_hashmap_get(members, gr->gr_name);
if (a) {
_cleanup_strv_free_ char **l = NULL;
bool added = false;
@@ -307,7 +308,7 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
assert(sg);
assert(gshadow);
- a = hashmap_get(members, sg->sg_namp);
+ a = ordered_hashmap_get(members, sg->sg_namp);
if (a) {
_cleanup_strv_free_ char **l = NULL;
bool added = false;
@@ -387,7 +388,7 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char
Item *i;
int r;
- if (hashmap_size(todo_uids) == 0)
+ if (ordered_hashmap_size(todo_uids) == 0)
return 0;
r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
@@ -405,13 +406,13 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char
errno = 0;
while ((pw = fgetpwent(original))) {
- i = hashmap_get(users, pw->pw_name);
+ i = ordered_hashmap_get(users, pw->pw_name);
if (i && i->todo_user) {
log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name);
return -EEXIST;
}
- if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
+ if (ordered_hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid);
return -EEXIST;
}
@@ -432,7 +433,7 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char
return -errno;
}
- HASHMAP_FOREACH(i, todo_uids, iterator) {
+ ORDERED_HASHMAP_FOREACH(i, todo_uids, iterator) {
struct passwd n = {
.pw_name = i->name,
.pw_uid = i->uid,
@@ -474,7 +475,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char
Item *i;
int r;
- if (hashmap_size(todo_uids) == 0)
+ if (ordered_hashmap_size(todo_uids) == 0)
return 0;
r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
@@ -494,7 +495,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char
errno = 0;
while ((sp = fgetspent(original))) {
- i = hashmap_get(users, sp->sp_namp);
+ i = ordered_hashmap_get(users, sp->sp_namp);
if (i && i->todo_user) {
/* we will update the existing entry */
sp->sp_lstchg = lstchg;
@@ -502,7 +503,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char
/* only the /etc/shadow stage is left, so we can
* safely remove the item from the todo set */
i->todo_user = false;
- hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
+ ordered_hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
}
errno = 0;
@@ -521,7 +522,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char
return -errno;
}
- HASHMAP_FOREACH(i, todo_uids, iterator) {
+ ORDERED_HASHMAP_FOREACH(i, todo_uids, iterator) {
struct spwd n = {
.sp_namp = i->name,
.sp_pwdp = (char*) "!!",
@@ -558,7 +559,7 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
Item *i;
int r;
- if (hashmap_size(todo_gids) == 0 && hashmap_size(members) == 0)
+ if (ordered_hashmap_size(todo_gids) == 0 && ordered_hashmap_size(members) == 0)
return 0;
r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
@@ -580,13 +581,13 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
* entries anyway here, let's make an extra verification
* step that we don't generate duplicate entries. */
- i = hashmap_get(groups, gr->gr_name);
+ i = ordered_hashmap_get(groups, gr->gr_name);
if (i && i->todo_group) {
log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name);
return -EEXIST;
}
- if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
+ if (ordered_hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid);
return -EEXIST;
}
@@ -609,7 +610,7 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
return -errno;
}
- HASHMAP_FOREACH(i, todo_gids, iterator) {
+ ORDERED_HASHMAP_FOREACH(i, todo_gids, iterator) {
struct group n = {
.gr_name = i->name,
.gr_gid = i->gid,
@@ -645,7 +646,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch
Item *i;
int r;
- if (hashmap_size(todo_gids) == 0 && hashmap_size(members) == 0)
+ if (ordered_hashmap_size(todo_gids) == 0 && ordered_hashmap_size(members) == 0)
return 0;
r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
@@ -663,7 +664,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch
errno = 0;
while ((sg = fgetsgent(original))) {
- i = hashmap_get(groups, sg->sg_namp);
+ i = ordered_hashmap_get(groups, sg->sg_namp);
if (i && i->todo_group) {
log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp);
return -EEXIST;
@@ -687,7 +688,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch
return -errno;
}
- HASHMAP_FOREACH(i, todo_gids, iterator) {
+ ORDERED_HASHMAP_FOREACH(i, todo_gids, iterator) {
struct sgrp n = {
.sg_namp = i->name,
.sg_passwd = (char*) "!!",
@@ -807,12 +808,12 @@ static int uid_is_ok(uid_t uid, const char *name) {
Item *i;
/* Let's see if we already have assigned the UID a second time */
- if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
+ if (ordered_hashmap_get(todo_uids, UID_TO_PTR(uid)))
return 0;
/* Try to avoid using uids that are already used by a group
* that doesn't have the same name as our new user. */
- i = hashmap_get(todo_gids, GID_TO_PTR(uid));
+ i = ordered_hashmap_get(todo_gids, GID_TO_PTR(uid));
if (i && !streq(i->name, name))
return 0;
@@ -1012,11 +1013,11 @@ static int add_user(Item *i) {
i->uid = search_uid;
}
- r = hashmap_ensure_allocated(&todo_uids, NULL);
+ r = ordered_hashmap_ensure_allocated(&todo_uids, NULL);
if (r < 0)
return log_oom();
- r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
+ r = ordered_hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
if (r < 0)
return log_oom();
@@ -1030,11 +1031,11 @@ static int gid_is_ok(gid_t gid) {
struct group *g;
struct passwd *p;
- if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
+ if (ordered_hashmap_get(todo_gids, GID_TO_PTR(gid)))
return 0;
/* Avoid reusing gids that are already used by a different user */
- if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
+ if (ordered_hashmap_get(todo_uids, UID_TO_PTR(gid)))
return 0;
if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
@@ -1098,6 +1099,18 @@ static int add_group(Item *i) {
r = gid_is_ok(i->gid);
if (r < 0)
return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
+ if (i->gid_must_exist) {
+ /* If we require the gid to already exist we can return here:
+ * r > 0: means the gid does not exist -> fail
+ * r == 0: means the gid exists -> nothing more to do.
+ */
+ if (r > 0) {
+ log_error("Failed to create %s: please create GID %d", i->name, i->gid);
+ return -EINVAL;
+ }
+ if (r == 0)
+ return 0;
+ }
if (r == 0) {
log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
i->gid_set = false;
@@ -1157,11 +1170,11 @@ static int add_group(Item *i) {
i->gid = search_uid;
}
- r = hashmap_ensure_allocated(&todo_gids, NULL);
+ r = ordered_hashmap_ensure_allocated(&todo_gids, NULL);
if (r < 0)
return log_oom();
- r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
+ r = ordered_hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
if (r < 0)
return log_oom();
@@ -1185,30 +1198,8 @@ static int process_item(Item *i) {
return add_user(i);
- case ADD_GROUP: {
- Item *j;
-
- j = hashmap_get(users, i->name);
- if (j) {
- /* There's already user to be created for this
- * name, let's process that in one step */
-
- if (i->gid_set) {
- j->gid = i->gid;
- j->gid_set = true;
- }
-
- if (i->gid_path) {
- r = free_and_strdup(&j->gid_path, i->gid_path);
- if (r < 0)
- return log_oom();
- }
-
- return 0;
- }
-
+ case ADD_GROUP:
return add_group(i);
- }
default:
assert_not_reached("Unknown item type");
@@ -1237,15 +1228,15 @@ static int add_implicit(void) {
/* Implicitly create additional users and groups, if they were listed in "m" lines */
- HASHMAP_FOREACH_KEY(l, g, members, iterator) {
+ ORDERED_HASHMAP_FOREACH_KEY(l, g, members, iterator) {
Item *i;
char **m;
- i = hashmap_get(groups, g);
+ i = ordered_hashmap_get(groups, g);
if (!i) {
_cleanup_(item_freep) Item *j = NULL;
- r = hashmap_ensure_allocated(&groups, &string_hash_ops);
+ r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops);
if (r < 0)
return log_oom();
@@ -1258,7 +1249,7 @@ static int add_implicit(void) {
if (!j->name)
return log_oom();
- r = hashmap_put(groups, j->name, j);
+ r = ordered_hashmap_put(groups, j->name, j);
if (r < 0)
return log_oom();
@@ -1268,11 +1259,11 @@ static int add_implicit(void) {
STRV_FOREACH(m, l) {
- i = hashmap_get(users, *m);
+ i = ordered_hashmap_get(users, *m);
if (!i) {
_cleanup_(item_freep) Item *j = NULL;
- r = hashmap_ensure_allocated(&users, &string_hash_ops);
+ r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops);
if (r < 0)
return log_oom();
@@ -1285,7 +1276,7 @@ static int add_implicit(void) {
if (!j->name)
return log_oom();
- r = hashmap_put(users, j->name, j);
+ r = ordered_hashmap_put(users, j->name, j);
if (r < 0)
return log_oom();
@@ -1348,7 +1339,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
_cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL;
_cleanup_(item_freep) Item *i = NULL;
Item *existing;
- Hashmap *h;
+ OrderedHashmap *h;
int r;
const char *p;
@@ -1494,11 +1485,11 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return -EINVAL;
}
- r = hashmap_ensure_allocated(&members, &string_hash_ops);
+ r = ordered_hashmap_ensure_allocated(&members, &string_hash_ops);
if (r < 0)
return log_oom();
- l = hashmap_get(members, resolved_id);
+ l = ordered_hashmap_get(members, resolved_id);
if (l) {
/* A list for this group name already exists, let's append to it */
r = strv_push(&l, resolved_name);
@@ -1507,7 +1498,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
resolved_name = NULL;
- assert_se(hashmap_update(members, resolved_id, l) >= 0);
+ assert_se(ordered_hashmap_update(members, resolved_id, l) >= 0);
} else {
/* No list for this group name exists yet, create one */
@@ -1518,7 +1509,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
l[0] = resolved_name;
l[1] = NULL;
- r = hashmap_put(members, resolved_id, l);
+ r = ordered_hashmap_put(members, resolved_id, l);
if (r < 0) {
free(l);
return log_oom();
@@ -1536,7 +1527,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return -EINVAL;
}
- r = hashmap_ensure_allocated(&users, &string_hash_ops);
+ r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops);
if (r < 0)
return log_oom();
@@ -1551,11 +1542,18 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
path_kill_slashes(i->uid_path);
} else {
- r = parse_uid(resolved_id, &i->uid);
- if (r < 0) {
- log_error("Failed to parse UID: %s", id);
- return -EBADMSG;
+ _cleanup_free_ char *uid = NULL, *gid = NULL;
+ if (split_pair(resolved_id, ":", &uid, &gid) == 0) {
+ r = parse_gid(gid, &i->gid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
+ i->gid_set = true;
+ i->gid_must_exist = true;
+ free_and_replace(resolved_id, uid);
}
+ r = parse_uid(resolved_id, &i->uid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID: '%s': %m", id);
i->uid_set = true;
}
@@ -1586,7 +1584,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return -EINVAL;
}
- r = hashmap_ensure_allocated(&groups, &string_hash_ops);
+ r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops);
if (r < 0)
return log_oom();
@@ -1602,10 +1600,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
path_kill_slashes(i->gid_path);
} else {
r = parse_gid(resolved_id, &i->gid);
- if (r < 0) {
- log_error("Failed to parse GID: %s", id);
- return -EBADMSG;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
i->gid_set = true;
}
@@ -1622,7 +1618,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
i->name = resolved_name;
resolved_name = NULL;
- existing = hashmap_get(h, i->name);
+ existing = ordered_hashmap_get(h, i->name);
if (existing) {
/* Two identical items are fine */
@@ -1632,7 +1628,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return 0;
}
- r = hashmap_put(h, i->name, i);
+ r = ordered_hashmap_put(h, i->name, i);
if (r < 0)
return log_oom();
@@ -1853,10 +1849,10 @@ int main(int argc, char *argv[]) {
goto finish;
}
- HASHMAP_FOREACH(i, groups, iterator)
+ ORDERED_HASHMAP_FOREACH(i, groups, iterator)
process_item(i);
- HASHMAP_FOREACH(i, users, iterator)
+ ORDERED_HASHMAP_FOREACH(i, users, iterator)
process_item(i);
r = write_files();
@@ -1864,17 +1860,17 @@ int main(int argc, char *argv[]) {
log_error_errno(r, "Failed to write files: %m");
finish:
- hashmap_free_with_destructor(groups, item_free);
- hashmap_free_with_destructor(users, item_free);
+ ordered_hashmap_free_with_destructor(groups, item_free);
+ ordered_hashmap_free_with_destructor(users, item_free);
- while ((n = hashmap_first_key(members))) {
- strv_free(hashmap_steal_first(members));
+ while ((n = ordered_hashmap_first_key(members))) {
+ strv_free(ordered_hashmap_steal_first(members));
free(n);
}
- hashmap_free(members);
+ ordered_hashmap_free(members);
- hashmap_free(todo_uids);
- hashmap_free(todo_gids);
+ ordered_hashmap_free(todo_uids);
+ ordered_hashmap_free(todo_gids);
free_database(database_user, database_uid);
free_database(database_group, database_gid);
diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
index 51306b5625..087ba08559 100644
--- a/src/sysv-generator/sysv-generator.c
+++ b/src/sysv-generator/sysv-generator.c
@@ -950,7 +950,8 @@ int main(int argc, char *argv[]) {
if (argc > 1)
arg_dest = argv[3];
- log_set_target(LOG_TARGET_SAFE);
+ log_set_prohibit_ipc(true);
+ log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
diff --git a/src/test/meson.build b/src/test/meson.build
index 6bb5bd629e..1db8aa107d 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -67,7 +67,7 @@ tests += [
'src/test/test-helper.c'],
[libcore,
libudev,
- libsystemd_internal],
+ libshared],
[threads,
librt,
libseccomp,
@@ -119,6 +119,7 @@ tests += [
[['src/test/test-dns-domain.c'],
[libcore,
+ libshared,
libsystemd_network],
[]],
@@ -167,7 +168,7 @@ tests += [
[]],
[['src/test/test-copy.c'],
- [libshared_static],
+ [],
[]],
[['src/test/test-sigbus.c'],
@@ -376,6 +377,17 @@ tests += [
libselinux,
libblkid]],
+ [['src/test/test-watch-pid.c',
+ 'src/test/test-helper.c'],
+ [libcore,
+ libshared],
+ [libmount,
+ threads,
+ librt,
+ libseccomp,
+ libselinux,
+ libblkid]],
+
[['src/test/test-hashmap.c',
'src/test/test-hashmap-plain.c',
test_hashmap_ordered_c],
@@ -399,6 +411,10 @@ tests += [
[],
[]],
+ [['src/test/test-procfs-util.c'],
+ [],
+ []],
+
[['src/test/test-unaligned.c'],
[],
[]],
@@ -410,7 +426,7 @@ tests += [
[libcore,
libjournal_core,
libudev_core,
- libudev_internal,
+ libudev_static,
libsystemd_network,
libshared],
[threads,
@@ -499,7 +515,6 @@ tests += [
[],
'', 'manual'],
-
[['src/test/test-cgroup-mask.c',
'src/test/test-helper.c'],
[libcore,
@@ -610,7 +625,7 @@ tests += [
[['src/test/test-udev.c'],
[libudev_core,
- libudev_internal,
+ libudev_static,
libsystemd_network,
libshared],
[threads,
@@ -761,6 +776,10 @@ tests += [
[],
[threads]],
+ [['src/libsystemd/sd-bus/test-bus-watch-bind.c'],
+ [],
+ [threads], '', 'timeout=120'],
+
[['src/libsystemd/sd-bus/test-bus-chat.c'],
[],
[threads]],
@@ -772,7 +791,7 @@ tests += [
[['src/libsystemd/sd-bus/test-bus-error.c'],
[libshared_static,
- libsystemd_internal],
+ libsystemd_static],
[]],
[['src/libsystemd/sd-bus/test-bus-track.c'],
diff --git a/src/test/test-async.c b/src/test/test-async.c
index 2055ce25bf..87906b2e25 100644
--- a/src/test/test-async.c
+++ b/src/test/test-async.c
@@ -43,7 +43,7 @@ int main(int argc, char *argv[]) {
assert_se(asynchronous_job(async_func, NULL) >= 0);
- assert_se(asynchronous_sync() >= 0);
+ assert_se(asynchronous_sync(NULL) >= 0);
sleep(1);
diff --git a/src/test/test-capability.c b/src/test/test-capability.c
index e5db52d404..10cddaf552 100644
--- a/src/test/test-capability.c
+++ b/src/test/test-capability.c
@@ -80,7 +80,7 @@ static void fork_test(void (*test_func)(void)) {
assert_se(pid >= 0);
if (pid == 0) {
test_func();
- exit(0);
+ exit(EXIT_SUCCESS);
} else if (pid > 0) {
int status;
diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c
index 45eb3ef8b1..2248a30635 100644
--- a/src/test/test-cgroup-util.c
+++ b/src/test/test-cgroup-util.c
@@ -27,6 +27,7 @@
#include "parse-util.h"
#include "proc-cmdline.h"
#include "process-util.h"
+#include "special.h"
#include "stat-util.h"
#include "string-util.h"
#include "test-helper.h"
@@ -141,10 +142,10 @@ static void check_p_g_slice(const char *path, int code, const char *result) {
static void test_path_get_slice(void) {
check_p_g_slice("/user.slice", 0, "user.slice");
- check_p_g_slice("/foobar", 0, "-.slice");
+ check_p_g_slice("/foobar", 0, SPECIAL_ROOT_SLICE);
check_p_g_slice("/user.slice/user-waldo.slice", 0, "user-waldo.slice");
- check_p_g_slice("", 0, "-.slice");
- check_p_g_slice("foobar", 0, "-.slice");
+ check_p_g_slice("", 0, SPECIAL_ROOT_SLICE);
+ check_p_g_slice("foobar", 0, SPECIAL_ROOT_SLICE);
check_p_g_slice("foobar.slice", 0, "foobar.slice");
check_p_g_slice("foo.slice/foo-bar.slice/waldo.service", 0, "foo-bar.slice");
}
@@ -165,10 +166,10 @@ static void test_path_get_user_slice(void) {
check_p_g_u_slice("foobar.slice", -ENXIO, NULL);
check_p_g_u_slice("foo.slice/foo-bar.slice/waldo.service", -ENXIO, NULL);
- check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service", 0, "-.slice");
- check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/", 0, "-.slice");
- check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service///", 0, "-.slice");
- check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/waldo.service", 0, "-.slice");
+ check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service", 0, SPECIAL_ROOT_SLICE);
+ check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/", 0, SPECIAL_ROOT_SLICE);
+ check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service///", 0, SPECIAL_ROOT_SLICE);
+ check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/waldo.service", 0, SPECIAL_ROOT_SLICE);
check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/piep.slice/foo.service", 0, "piep.slice");
check_p_g_u_slice("/foo.slice//foo-bar.slice/user@1000.service/piep.slice//piep-pap.slice//foo.service", 0, "piep-pap.slice");
}
@@ -274,7 +275,7 @@ static void test_slice_to_path(void) {
test_slice_to_path_one("foobar.slice", "foobar.slice", 0);
test_slice_to_path_one("foobar-waldo.slice", "foobar.slice/foobar-waldo.slice", 0);
test_slice_to_path_one("foobar-waldo.service", NULL, -EINVAL);
- test_slice_to_path_one("-.slice", "", 0);
+ test_slice_to_path_one(SPECIAL_ROOT_SLICE, "", 0);
test_slice_to_path_one("--.slice", NULL, -EINVAL);
test_slice_to_path_one("-", NULL, -EINVAL);
test_slice_to_path_one("-foo-.slice", NULL, -EINVAL);
diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c
index 2ae95db162..b2440fc3a9 100644
--- a/src/test/test-cgroup.c
+++ b/src/test/test-cgroup.c
@@ -23,6 +23,7 @@
#include "cgroup-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "string-util.h"
#include "util.h"
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
index d43db3a7cd..ad64a2bb36 100644
--- a/src/test/test-condition.c
+++ b/src/test/test-condition.c
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <sys/types.h>
+#include <sys/utsname.h>
#include <unistd.h>
#include "sd-id128.h"
@@ -28,6 +29,7 @@
#include "apparmor-util.h"
#include "architecture.h"
#include "audit-util.h"
+#include "cgroup-util.h"
#include "condition.h"
#include "hostname-util.h"
#include "id128-util.h"
@@ -35,11 +37,13 @@
#include "log.h"
#include "macro.h"
#include "selinux-util.h"
+#include "set.h"
#include "smack-util.h"
+#include "string-util.h"
#include "strv.h"
-#include "virt.h"
-#include "util.h"
#include "user-util.h"
+#include "util.h"
+#include "virt.h"
static void test_condition_test_path(void) {
Condition *condition;
@@ -125,6 +129,77 @@ static void test_condition_test_path(void) {
condition_free(condition);
}
+static int test_condition_test_control_group_controller(void) {
+ Condition *condition;
+ CGroupMask system_mask;
+ CGroupController controller;
+ _cleanup_free_ char *controller_name = NULL;
+ int r;
+
+ r = cg_unified_flush();
+ if (r < 0) {
+ log_notice_errno(r, "Skipping ConditionControlGroupController tests: %m");
+ return EXIT_TEST_SKIP;
+ }
+
+ /* Invalid controllers are ignored */
+ condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, "thisisnotarealcontroller", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, "thisisnotarealcontroller", false, true);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ assert_se(cg_mask_supported(&system_mask) >= 0);
+
+ /* Individual valid controllers one by one */
+ for (controller = 0; controller < _CGROUP_CONTROLLER_MAX; controller++) {
+ const char *local_controller_name = cgroup_controller_to_string(controller);
+ log_info("chosen controller is '%s'", local_controller_name);
+ if (system_mask & CGROUP_CONTROLLER_TO_MASK(controller)) {
+ log_info("this controller is available");
+ condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, true);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+ } else {
+ log_info("this controller is unavailable");
+ condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, true);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+ }
+ }
+
+ /* Multiple valid controllers at the same time */
+ assert_se(cg_mask_to_string(system_mask, &controller_name) >= 0);
+
+ condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, strempty(controller_name), false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, strempty(controller_name), false, true);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ return EXIT_SUCCESS;
+}
+
static void test_condition_test_ac_power(void) {
Condition *condition;
@@ -225,6 +300,126 @@ static void test_condition_test_kernel_command_line(void) {
condition_free(condition);
}
+static void test_condition_test_kernel_version(void) {
+ Condition *condition;
+ struct utsname u;
+ const char *v;
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "*thisreallyshouldntbeinthekernelversion*", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "*", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ assert_se(uname(&u) >= 0);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ strshorten(u.release, 4);
+ strcpy(strchr(u.release, 0), "*");
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ /* 0.1.2 would be a very very very old kernel */
+ condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, ">= 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "< 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "<= 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "= 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ /* 4711.8.15 is a very very very future kernel */
+ condition = condition_new(CONDITION_KERNEL_VERSION, "< 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "<= 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "= 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "> 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, ">= 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ assert_se(uname(&u) >= 0);
+
+ v = strjoina(">=", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ v = strjoina("= ", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ v = strjoina("<=", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ v = strjoina("> ", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ v = strjoina("< ", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+}
+
static void test_condition_test_null(void) {
Condition *condition;
@@ -483,11 +678,13 @@ int main(int argc, char *argv[]) {
test_condition_test_host();
test_condition_test_architecture();
test_condition_test_kernel_command_line();
+ test_condition_test_kernel_version();
test_condition_test_null();
test_condition_test_security();
test_condition_test_virtualization();
test_condition_test_user();
test_condition_test_group();
+ test_condition_test_control_group_controller();
return 0;
}
diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c
index 84ab083e87..3e7c197cfe 100644
--- a/src/test/test-extract-word.c
+++ b/src/test/test-extract-word.c
@@ -19,6 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <stdlib.h>
#include <string.h>
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index 86d963c4c7..9f3a500080 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -22,21 +22,25 @@
#include "alloc-util.h"
#include "fd-util.h"
+#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "id128-util.h"
#include "macro.h"
#include "mkdir.h"
#include "path-util.h"
#include "rm-rf.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
+#include "user-util.h"
#include "util.h"
static void test_chase_symlinks(void) {
_cleanup_free_ char *result = NULL;
char temp[] = "/tmp/test-chase.XXXXXX";
const char *top, *p, *pslash, *q, *qslash;
- int r;
+ int r, pfd;
assert_se(mkdtemp(temp));
@@ -235,6 +239,55 @@ static void test_chase_symlinks(void) {
r = chase_symlinks(p, NULL, 0, &result);
assert_se(r == -ENOENT);
+ if (geteuid() == 0) {
+ p = strjoina(temp, "/priv1");
+ assert_se(mkdir(p, 0755) >= 0);
+
+ q = strjoina(p, "/priv2");
+ assert_se(mkdir(q, 0755) >= 0);
+
+ assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
+
+ assert_se(chown(q, UID_NOBODY, GID_NOBODY) >= 0);
+ assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
+
+ assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0);
+ assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
+
+ assert_se(chown(q, 0, 0) >= 0);
+ assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -EPERM);
+
+ assert_se(rmdir(q) >= 0);
+ assert_se(symlink("/etc/passwd", q) >= 0);
+ assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -EPERM);
+
+ assert_se(chown(p, 0, 0) >= 0);
+ assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
+ }
+
+ p = strjoina(temp, "/machine-id-test");
+ assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
+
+ pfd = chase_symlinks(p, NULL, CHASE_OPEN, NULL);
+ if (pfd != -ENOENT) {
+ char procfs[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(pfd) + 1];
+ _cleanup_close_ int fd = -1;
+ sd_id128_t a, b;
+
+ assert_se(pfd >= 0);
+
+ xsprintf(procfs, "/proc/self/fd/%i", pfd);
+
+ fd = open(procfs, O_RDONLY|O_CLOEXEC);
+ assert_se(fd >= 0);
+
+ safe_close(pfd);
+
+ assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0);
+ assert_se(sd_id128_get_machine(&b) >= 0);
+ assert_se(sd_id128_equal(a, b));
+ }
+
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
@@ -273,7 +326,7 @@ static void test_readlink_and_make_absolute(void) {
free(r);
assert_se(unlink(name_alias) >= 0);
- assert_se(pwd = get_current_dir_name());
+ assert_se(safe_getcwd(&pwd) >= 0);
assert_se(chdir(tempdir) >= 0);
assert_se(symlink(name2, name_alias) >= 0);
@@ -388,6 +441,92 @@ static void test_access_fd(void) {
}
}
+static void test_touch_file(void) {
+ uid_t test_uid, test_gid;
+ _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
+ struct stat st;
+ const char *a;
+ usec_t test_mtime;
+
+ test_uid = geteuid() == 0 ? 65534 : getuid();
+ test_gid = geteuid() == 0 ? 65534 : getgid();
+
+ test_mtime = usec_sub_unsigned(now(CLOCK_REALTIME), USEC_PER_WEEK);
+
+ assert_se(mkdtemp_malloc("/dev/shm/touch-file-XXXXXX", &p) >= 0);
+
+ a = strjoina(p, "/regular");
+ assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
+ assert_se(lstat(a, &st) >= 0);
+ assert_se(st.st_uid == test_uid);
+ assert_se(st.st_gid == test_gid);
+ assert_se(S_ISREG(st.st_mode));
+ assert_se((st.st_mode & 0777) == 0640);
+ assert_se(timespec_load(&st.st_mtim) == test_mtime);
+
+ a = strjoina(p, "/dir");
+ assert_se(mkdir(a, 0775) >= 0);
+ assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
+ assert_se(lstat(a, &st) >= 0);
+ assert_se(st.st_uid == test_uid);
+ assert_se(st.st_gid == test_gid);
+ assert_se(S_ISDIR(st.st_mode));
+ assert_se((st.st_mode & 0777) == 0640);
+ assert_se(timespec_load(&st.st_mtim) == test_mtime);
+
+ a = strjoina(p, "/fifo");
+ assert_se(mkfifo(a, 0775) >= 0);
+ assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
+ assert_se(lstat(a, &st) >= 0);
+ assert_se(st.st_uid == test_uid);
+ assert_se(st.st_gid == test_gid);
+ assert_se(S_ISFIFO(st.st_mode));
+ assert_se((st.st_mode & 0777) == 0640);
+ assert_se(timespec_load(&st.st_mtim) == test_mtime);
+
+ a = strjoina(p, "/sock");
+ assert_se(mknod(a, 0775 | S_IFSOCK, 0) >= 0);
+ assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
+ assert_se(lstat(a, &st) >= 0);
+ assert_se(st.st_uid == test_uid);
+ assert_se(st.st_gid == test_gid);
+ assert_se(S_ISSOCK(st.st_mode));
+ assert_se((st.st_mode & 0777) == 0640);
+ assert_se(timespec_load(&st.st_mtim) == test_mtime);
+
+ if (geteuid() == 0) {
+ a = strjoina(p, "/cdev");
+ assert_se(mknod(a, 0775 | S_IFCHR, makedev(0, 0)) >= 0);
+ assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
+ assert_se(lstat(a, &st) >= 0);
+ assert_se(st.st_uid == test_uid);
+ assert_se(st.st_gid == test_gid);
+ assert_se(S_ISCHR(st.st_mode));
+ assert_se((st.st_mode & 0777) == 0640);
+ assert_se(timespec_load(&st.st_mtim) == test_mtime);
+
+ a = strjoina(p, "/bdev");
+ assert_se(mknod(a, 0775 | S_IFBLK, makedev(0, 0)) >= 0);
+ assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
+ assert_se(lstat(a, &st) >= 0);
+ assert_se(st.st_uid == test_uid);
+ assert_se(st.st_gid == test_gid);
+ assert_se(S_ISBLK(st.st_mode));
+ assert_se((st.st_mode & 0777) == 0640);
+ assert_se(timespec_load(&st.st_mtim) == test_mtime);
+ }
+
+ a = strjoina(p, "/lnk");
+ assert_se(symlink("target", a) >= 0);
+ assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
+ assert_se(lstat(a, &st) >= 0);
+ assert_se(st.st_uid == test_uid);
+ assert_se(st.st_gid == test_gid);
+ assert_se(S_ISLNK(st.st_mode));
+ assert_se((st.st_mode & 0777) == 0640);
+ assert_se(timespec_load(&st.st_mtim) == test_mtime);
+}
+
int main(int argc, char *argv[]) {
test_unlink_noerrno();
test_get_files_in_directory();
@@ -396,6 +535,7 @@ int main(int argc, char *argv[]) {
test_chase_symlinks();
test_dot_or_dot_dot();
test_access_fd();
+ test_touch_file();
return 0;
}
diff --git a/src/test/test-hash.c b/src/test/test-hash.c
index f3b4258d6b..0366727476 100644
--- a/src/test/test-hash.c
+++ b/src/test/test-hash.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <stdio.h>
#include "alloc-util.h"
diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c
index 4f19cb406f..3e25a0bac8 100644
--- a/src/test/test-hexdecoct.c
+++ b/src/test/test-hexdecoct.c
@@ -18,6 +18,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+
#include "alloc-util.h"
#include "hexdecoct.h"
#include "macro.h"
diff --git a/src/test/test-log.c b/src/test/test-log.c
index 9468349cba..fd19899480 100644
--- a/src/test/test-log.c
+++ b/src/test/test-log.c
@@ -23,6 +23,7 @@
#include "format-util.h"
#include "log.h"
+#include "process-util.h"
#include "util.h"
assert_cc(LOG_REALM_REMOVE_LEVEL(LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, LOG_FTP | LOG_DEBUG))
diff --git a/src/test/test-ns.c b/src/test/test-ns.c
index 76e2b38b17..87b4facb85 100644
--- a/src/test/test-ns.c
+++ b/src/test/test-ns.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c
index 8259e133c3..9375002133 100644
--- a/src/test/test-parse-util.c
+++ b/src/test/test-parse-util.c
@@ -19,6 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <locale.h>
#include <math.h>
diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c
index a38f917961..72edcbb7d6 100644
--- a/src/test/test-process-util.c
+++ b/src/test/test-process-util.c
@@ -38,6 +38,7 @@
#include "macro.h"
#include "parse-util.h"
#include "process-util.h"
+#include "signal-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "terminal-util.h"
@@ -353,7 +354,7 @@ static void test_get_process_cmdline_harder(void) {
line = mfree(line);
safe_close(fd);
- _exit(0);
+ _exit(EXIT_SUCCESS);
}
static void test_rename_process_now(const char *p, int ret) {
@@ -462,7 +463,7 @@ static void test_getpid_cached(void) {
c = getpid();
assert_se(a == b && a == c);
- _exit(0);
+ _exit(EXIT_SUCCESS);
}
d = raw_getpid();
@@ -497,6 +498,47 @@ static void test_getpid_measure(void) {
log_info("getpid_cached(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
}
+static void test_safe_fork(void) {
+ siginfo_t status;
+ pid_t pid;
+ int r;
+
+ BLOCK_SIGNALS(SIGCHLD);
+
+ r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NULL_STDIO|FORK_REOPEN_LOG, &pid);
+ assert_se(r >= 0);
+
+ if (r == 0) {
+ /* child */
+ usleep(100 * USEC_PER_MSEC);
+
+ _exit(88);
+ }
+
+ assert_se(wait_for_terminate(pid, &status) >= 0);
+ assert_se(status.si_code == CLD_EXITED);
+ assert_se(status.si_status == 88);
+}
+
+static void test_pid_to_ptr(void) {
+
+ assert_se(PTR_TO_PID(NULL) == 0);
+ assert_se(PID_TO_PTR(0) == NULL);
+
+ assert_se(PTR_TO_PID(PID_TO_PTR(1)) == 1);
+ assert_se(PTR_TO_PID(PID_TO_PTR(2)) == 2);
+ assert_se(PTR_TO_PID(PID_TO_PTR(-1)) == -1);
+ assert_se(PTR_TO_PID(PID_TO_PTR(-2)) == -2);
+
+ assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MAX)) == INT16_MAX);
+ assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MIN)) == INT16_MIN);
+
+#if SIZEOF_PID_T >= 4
+ assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MAX)) == INT32_MAX);
+ assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MIN)) == INT32_MIN);
+#endif
+}
+
int main(int argc, char *argv[]) {
log_set_max_level(LOG_DEBUG);
@@ -523,6 +565,8 @@ int main(int argc, char *argv[]) {
test_rename_process();
test_getpid_cached();
test_getpid_measure();
+ test_safe_fork();
+ test_pid_to_ptr();
return 0;
}
diff --git a/src/test/test-procfs-util.c b/src/test/test-procfs-util.c
new file mode 100644
index 0000000000..a253182517
--- /dev/null
+++ b/src/test/test-procfs-util.c
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+
+#include "log.h"
+#include "procfs-util.h"
+
+int main(int argc, char *argv[]) {
+ uint64_t v;
+ int r;
+
+ log_parse_environment();
+ log_open();
+
+ assert_se(procfs_tasks_get_current(&v) >= 0);
+ log_info("Current number of tasks: %" PRIu64, v);
+
+ assert_se(procfs_tasks_get_limit(&v) >= 0);
+ log_info("Limit of tasks: %" PRIu64, v);
+ assert_se(v > 0);
+ assert_se(procfs_tasks_set_limit(v) >= 0);
+
+ if (v > 100) {
+ uint64_t w;
+ r = procfs_tasks_set_limit(v-1);
+ assert_se(IN_SET(r, 0, -EPERM, -EACCES, -EROFS));
+
+ assert_se(procfs_tasks_get_limit(&w) >= 0);
+ assert_se((r == 0 && w == v - 1) || (r < 0 && w == v));
+
+ assert_se(procfs_tasks_set_limit(v) >= 0);
+
+ assert_se(procfs_tasks_get_limit(&w) >= 0);
+ assert_se(v == w);
+ }
+
+ return 0;
+}
diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c
index 36b49ebc71..aed307077e 100644
--- a/src/test/test-seccomp.c
+++ b/src/test/test-seccomp.c
@@ -141,7 +141,7 @@ static void test_filter_sets(void) {
_exit(EXIT_SUCCESS);
}
- assert_se(wait_for_terminate_and_warn(syscall_filter_sets[i].name, pid, true) == EXIT_SUCCESS);
+ assert_se(wait_for_terminate_and_check(syscall_filter_sets[i].name, pid, WAIT_LOG) == EXIT_SUCCESS);
}
}
@@ -227,7 +227,7 @@ static void test_restrict_namespace(void) {
_exit(EXIT_SUCCESS);
}
- assert_se(wait_for_terminate_and_warn("nsseccomp", pid, true) == EXIT_SUCCESS);
+ assert_se(wait_for_terminate_and_check("nsseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
}
static void test_protect_sysctl(void) {
@@ -260,7 +260,7 @@ static void test_protect_sysctl(void) {
_exit(EXIT_SUCCESS);
}
- assert_se(wait_for_terminate_and_warn("sysctlseccomp", pid, true) == EXIT_SUCCESS);
+ assert_se(wait_for_terminate_and_check("sysctlseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
}
static void test_restrict_address_families(void) {
@@ -343,7 +343,7 @@ static void test_restrict_address_families(void) {
_exit(EXIT_SUCCESS);
}
- assert_se(wait_for_terminate_and_warn("socketseccomp", pid, true) == EXIT_SUCCESS);
+ assert_se(wait_for_terminate_and_check("socketseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
}
static void test_restrict_realtime(void) {
@@ -381,7 +381,7 @@ static void test_restrict_realtime(void) {
_exit(EXIT_SUCCESS);
}
- assert_se(wait_for_terminate_and_warn("realtimeseccomp", pid, true) == EXIT_SUCCESS);
+ assert_se(wait_for_terminate_and_check("realtimeseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
}
static void test_memory_deny_write_execute_mmap(void) {
@@ -424,7 +424,7 @@ static void test_memory_deny_write_execute_mmap(void) {
_exit(EXIT_SUCCESS);
}
- assert_se(wait_for_terminate_and_warn("memoryseccomp-mmap", pid, true) == EXIT_SUCCESS);
+ assert_se(wait_for_terminate_and_check("memoryseccomp-mmap", pid, WAIT_LOG) == EXIT_SUCCESS);
}
static void test_memory_deny_write_execute_shmat(void) {
@@ -471,7 +471,7 @@ static void test_memory_deny_write_execute_shmat(void) {
_exit(EXIT_SUCCESS);
}
- assert_se(wait_for_terminate_and_warn("memoryseccomp-shmat", pid, true) == EXIT_SUCCESS);
+ assert_se(wait_for_terminate_and_check("memoryseccomp-shmat", pid, WAIT_LOG) == EXIT_SUCCESS);
}
static void test_restrict_archs(void) {
@@ -505,7 +505,7 @@ static void test_restrict_archs(void) {
_exit(EXIT_SUCCESS);
}
- assert_se(wait_for_terminate_and_warn("archseccomp", pid, true) == EXIT_SUCCESS);
+ assert_se(wait_for_terminate_and_check("archseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
}
static void test_load_syscall_filter_set_raw(void) {
@@ -596,7 +596,7 @@ static void test_load_syscall_filter_set_raw(void) {
_exit(EXIT_SUCCESS);
}
- assert_se(wait_for_terminate_and_warn("syscallrawseccomp", pid, true) == EXIT_SUCCESS);
+ assert_se(wait_for_terminate_and_check("syscallrawseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
}
static void test_lock_personality(void) {
@@ -643,7 +643,7 @@ static void test_lock_personality(void) {
_exit(EXIT_SUCCESS);
}
- assert_se(wait_for_terminate_and_warn("lockpersonalityseccomp", pid, true) == EXIT_SUCCESS);
+ assert_se(wait_for_terminate_and_check("lockpersonalityseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
}
static void test_filter_sets_ordered(void) {
diff --git a/src/test/test-signal-util.c b/src/test/test-signal-util.c
index 13a1d2ba1f..f4b19ed69d 100644
--- a/src/test/test-signal-util.c
+++ b/src/test/test-signal-util.c
@@ -23,6 +23,7 @@
#include "macro.h"
#include "signal-util.h"
+#include "process-util.h"
static void test_block_signals(void) {
sigset_t ss;
diff --git a/src/test/test-sizeof.c b/src/test/test-sizeof.c
index f472edcfa1..aed6db8423 100644
--- a/src/test/test-sizeof.c
+++ b/src/test/test-sizeof.c
@@ -19,6 +19,7 @@
***/
#include <stdio.h>
+#include <string.h>
#include "time-util.h"
@@ -62,6 +63,7 @@ int main(void) {
info(usec_t);
info(__time_t);
info(pid_t);
+ info(uid_t);
info(gid_t);
info(enum Enum);
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index 6a91baf6d2..d1ab7486ed 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -18,12 +18,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <sys/types.h>
+#include <unistd.h>
+#include <grp.h>
+
#include "alloc-util.h"
#include "async.h"
#include "fd-util.h"
#include "in-addr-util.h"
#include "log.h"
#include "macro.h"
+#include "process-util.h"
#include "socket-util.h"
#include "string-util.h"
#include "util.h"
@@ -474,6 +479,68 @@ static void test_in_addr_is_multicast(void) {
assert_se(in_addr_is_multicast(f, &b) == 0);
}
+static void test_getpeercred_getpeergroups(void) {
+ int r;
+
+ r = safe_fork("(getpeercred)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ assert_se(r >= 0);
+
+ if (r == 0) {
+ static const gid_t gids[] = { 3, 4, 5, 6, 7 };
+ gid_t *test_gids;
+ _cleanup_free_ gid_t *peer_groups = NULL;
+ size_t n_test_gids;
+ uid_t test_uid;
+ gid_t test_gid;
+ struct ucred ucred;
+ int pair[2];
+
+ if (geteuid() == 0) {
+ test_uid = 1;
+ test_gid = 2;
+ test_gids = (gid_t*) gids;
+ n_test_gids = ELEMENTSOF(gids);
+
+ assert_se(setgroups(n_test_gids, test_gids) >= 0);
+ assert_se(setresgid(test_gid, test_gid, test_gid) >= 0);
+ assert_se(setresuid(test_uid, test_uid, test_uid) >= 0);
+
+ } else {
+ long ngroups_max;
+
+ test_uid = getuid();
+ test_gid = getgid();
+
+ ngroups_max = sysconf(_SC_NGROUPS_MAX);
+ assert(ngroups_max > 0);
+
+ test_gids = newa(gid_t, ngroups_max);
+
+ r = getgroups(ngroups_max, test_gids);
+ assert_se(r >= 0);
+ n_test_gids = (size_t) r;
+ }
+
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0);
+
+ assert_se(getpeercred(pair[0], &ucred) >= 0);
+
+ assert_se(ucred.uid == test_uid);
+ assert_se(ucred.gid == test_gid);
+ assert_se(ucred.pid == getpid_cached());
+
+ r = getpeergroups(pair[0], &peer_groups);
+ assert_se(r >= 0 || IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT));
+
+ if (r >= 0) {
+ assert_se((size_t) r == n_test_gids);
+ assert_se(memcmp(peer_groups, test_gids, sizeof(gid_t) * n_test_gids) == 0);
+ }
+
+ safe_close_pair(pair);
+ }
+}
+
int main(int argc, char *argv[]) {
log_set_max_level(LOG_DEBUG);
@@ -502,5 +569,7 @@ int main(int argc, char *argv[]) {
test_in_addr_is_multicast();
+ test_getpeercred_getpeergroups();
+
return 0;
}
diff --git a/src/test/test-strip-tab-ansi.c b/src/test/test-strip-tab-ansi.c
index aabb7c40e7..838a6e4db6 100644
--- a/src/test/test-strip-tab-ansi.c
+++ b/src/test/test-strip-tab-ansi.c
@@ -28,24 +28,24 @@ int main(int argc, char *argv[]) {
char *p;
assert_se(p = strdup("\tFoobar\tbar\twaldo\t"));
- assert_se(strip_tab_ansi(&p, NULL));
+ assert_se(strip_tab_ansi(&p, NULL, NULL));
fprintf(stdout, "<%s>\n", p);
assert_se(streq(p, " Foobar bar waldo "));
free(p);
assert_se(p = strdup(ANSI_HIGHLIGHT "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL));
- assert_se(strip_tab_ansi(&p, NULL));
+ assert_se(strip_tab_ansi(&p, NULL, NULL));
fprintf(stdout, "<%s>\n", p);
assert_se(streq(p, "Hello world!"));
free(p);
assert_se(p = strdup("\x1B[\x1B[\t\x1B[" ANSI_HIGHLIGHT "\x1B[" "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL));
- assert_se(strip_tab_ansi(&p, NULL));
+ assert_se(strip_tab_ansi(&p, NULL, NULL));
assert_se(streq(p, "\x1B[\x1B[ \x1B[\x1B[Hello world!"));
free(p);
assert_se(p = strdup("\x1B[waldo"));
- assert_se(strip_tab_ansi(&p, NULL));
+ assert_se(strip_tab_ansi(&p, NULL, NULL));
assert_se(streq(p, "\x1B[waldo"));
free(p);
diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c
index c479eccb8b..8e57fe0461 100644
--- a/src/test/test-tmpfiles.c
+++ b/src/test/test-tmpfiles.c
@@ -29,6 +29,7 @@
#include "format-util.h"
#include "fs-util.h"
#include "log.h"
+#include "process-util.h"
#include "string-util.h"
#include "util.h"
diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c
index e24892b590..416542c83f 100644
--- a/src/test/test-unit-name.c
+++ b/src/test/test-unit-name.c
@@ -32,6 +32,7 @@
#include "manager.h"
#include "path-util.h"
#include "rm-rf.h"
+#include "special.h"
#include "specifier.h"
#include "string-util.h"
#include "test-helper.h"
@@ -338,7 +339,7 @@ static void test_unit_name_build(void) {
}
static void test_slice_name_is_valid(void) {
- assert_se(slice_name_is_valid("-.slice"));
+ assert_se(slice_name_is_valid(SPECIAL_ROOT_SLICE));
assert_se(slice_name_is_valid("foo.slice"));
assert_se(slice_name_is_valid("foo-bar.slice"));
assert_se(slice_name_is_valid("foo-bar-baz.slice"));
@@ -356,7 +357,7 @@ static void test_build_subslice(void) {
char *a;
char *b;
- assert_se(slice_build_subslice("-.slice", "foo", &a) >= 0);
+ assert_se(slice_build_subslice(SPECIAL_ROOT_SLICE, "foo", &a) >= 0);
assert_se(slice_build_subslice(a, "bar", &b) >= 0);
free(a);
assert_se(slice_build_subslice(b, "barfoo", &a) >= 0);
@@ -378,8 +379,8 @@ static void test_build_parent_slice_one(const char *name, const char *expect, in
}
static void test_build_parent_slice(void) {
- test_build_parent_slice_one("-.slice", NULL, 0);
- test_build_parent_slice_one("foo.slice", "-.slice", 1);
+ test_build_parent_slice_one(SPECIAL_ROOT_SLICE, NULL, 0);
+ test_build_parent_slice_one("foo.slice", SPECIAL_ROOT_SLICE, 1);
test_build_parent_slice_one("foo-bar.slice", "foo.slice", 1);
test_build_parent_slice_one("foo-bar-baz.slice", "foo-bar.slice", 1);
test_build_parent_slice_one("foo-bar--baz.slice", NULL, -EINVAL);
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 2124511bf0..21d90f0888 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -28,6 +28,7 @@
#include "fileio.h"
#include "fs-util.h"
#include "parse-util.h"
+#include "process-util.h"
#include "raw-clone.h"
#include "rm-rf.h"
#include "string-util.h"
diff --git a/src/test/test-watch-pid.c b/src/test/test-watch-pid.c
new file mode 100644
index 0000000000..ed6c3d05cc
--- /dev/null
+++ b/src/test/test-watch-pid.c
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "log.h"
+#include "manager.h"
+#include "rm-rf.h"
+#include "test-helper.h"
+#include "tests.h"
+
+int main(int argc, char *argv[]) {
+ _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
+ Unit *a, *b, *c, *u;
+ Manager *m;
+ int r;
+
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
+ if (getuid() != 0) {
+ log_notice("Not running as root, skipping kernel related tests.");
+ return EXIT_TEST_SKIP;
+ }
+
+ r = enter_cgroup_subroot();
+ if (r == -ENOMEDIUM) {
+ log_notice("cgroupfs not available, skipping tests");
+ return EXIT_TEST_SKIP;
+ }
+
+ assert_se(set_unit_path(get_testdata_dir("")) >= 0);
+ assert_se(runtime_dir = setup_fake_runtime_dir());
+
+ assert_se(manager_new(UNIT_FILE_USER, true, &m) >= 0);
+ assert_se(manager_startup(m, NULL, NULL) >= 0);
+
+ assert_se(a = unit_new(m, sizeof(Service)));
+ assert_se(unit_add_name(a, "a.service") >= 0);
+ assert_se(set_isempty(a->pids));
+
+ assert_se(b = unit_new(m, sizeof(Service)));
+ assert_se(unit_add_name(b, "b.service") >= 0);
+ assert_se(set_isempty(b->pids));
+
+ assert_se(c = unit_new(m, sizeof(Service)));
+ assert_se(unit_add_name(c, "c.service") >= 0);
+ assert_se(set_isempty(c->pids));
+
+ assert_se(hashmap_isempty(m->watch_pids));
+ assert_se(manager_get_unit_by_pid(m, 4711) == NULL);
+
+ assert_se(unit_watch_pid(a, 4711) >= 0);
+ assert_se(manager_get_unit_by_pid(m, 4711) == a);
+
+ assert_se(unit_watch_pid(a, 4711) >= 0);
+ assert_se(manager_get_unit_by_pid(m, 4711) == a);
+
+ assert_se(unit_watch_pid(b, 4711) >= 0);
+ u = manager_get_unit_by_pid(m, 4711);
+ assert_se(u == a || u == b);
+
+ assert_se(unit_watch_pid(b, 4711) >= 0);
+ u = manager_get_unit_by_pid(m, 4711);
+ assert_se(u == a || u == b);
+
+ assert_se(unit_watch_pid(c, 4711) >= 0);
+ u = manager_get_unit_by_pid(m, 4711);
+ assert_se(u == a || u == b || u == c);
+
+ assert_se(unit_watch_pid(c, 4711) >= 0);
+ u = manager_get_unit_by_pid(m, 4711);
+ assert_se(u == a || u == b || u == c);
+
+ unit_unwatch_pid(b, 4711);
+ u = manager_get_unit_by_pid(m, 4711);
+ assert_se(u == a || u == c);
+
+ unit_unwatch_pid(b, 4711);
+ u = manager_get_unit_by_pid(m, 4711);
+ assert_se(u == a || u == c);
+
+ unit_unwatch_pid(a, 4711);
+ assert_se(manager_get_unit_by_pid(m, 4711) == c);
+
+ unit_unwatch_pid(a, 4711);
+ assert_se(manager_get_unit_by_pid(m, 4711) == c);
+
+ unit_unwatch_pid(c, 4711);
+ assert_se(manager_get_unit_by_pid(m, 4711) == NULL);
+
+ unit_unwatch_pid(c, 4711);
+ assert_se(manager_get_unit_by_pid(m, 4711) == NULL);
+
+ manager_free(m);
+
+ return 0;
+}
diff --git a/src/test/test-watchdog.c b/src/test/test-watchdog.c
index e068d1ddd4..ffcf408f57 100644
--- a/src/test/test-watchdog.c
+++ b/src/test/test-watchdog.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <string.h>
#include <unistd.h>
#include "env-util.h"
diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c
index d80a917870..19a382c1b2 100644
--- a/src/timedate/timedatectl.c
+++ b/src/timedate/timedatectl.c
@@ -129,8 +129,8 @@ static void print_status_info(const StatusInfo *i) {
"systemd-timesyncd.service active: %s\n"
" RTC in local TZ: %s\n",
strna(i->timezone), have_time && n > 0 ? a : "n/a",
- i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a",
yes_no(i->ntp_synced),
+ i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a",
yes_no(i->rtc_local));
if (i->rtc_local)
@@ -473,7 +473,7 @@ static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -493,6 +493,9 @@ int main(int argc, char *argv[]) {
r = timedatectl_main(bus, argc, argv);
finish:
+ /* make sure we terminate the bus connection first, and then close the
+ * pager, see issue #3543 for the details. */
+ sd_bus_flush_close_unref(bus);
pager_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index a55a929a49..822835cce9 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -675,9 +675,9 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
if (r < 0)
return log_error_errno(r, "Failed to register object: %m");
- r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0);
+ r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.timedate1", 0, NULL, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to register name: %m");
+ return log_error_errno(r, "Failed to request name: %m");
r = sd_bus_attach_event(bus, event, 0);
if (r < 0)
diff --git a/src/timesync/timesyncd-gperf.gperf b/src/timesync/timesyncd-gperf.gperf
index 7d4cd2808e..b5020276af 100644
--- a/src/timesync/timesyncd-gperf.gperf
+++ b/src/timesync/timesyncd-gperf.gperf
@@ -10,7 +10,7 @@ struct ConfigPerfItem;
%null_strings
%language=ANSI-C
%define slot-name section_and_lvalue
-%define hash-function-name timesyncdd_gperf_hash
+%define hash-function-name timesyncd_gperf_hash
%define lookup-function-name timesyncd_gperf_lookup
%readonly-tables
%omit-struct-type
diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c
index 8bd111fe0c..a6d336c461 100644
--- a/src/timesync/timesyncd-manager.c
+++ b/src/timesync/timesyncd-manager.c
@@ -552,7 +552,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
/* check our "time cookie" (we just stored nanoseconds in the fraction field) */
if (be32toh(ntpmsg.origin_time.sec) != m->trans_time.tv_sec + OFFSET_1900_1970 ||
- be32toh(ntpmsg.origin_time.frac) != m->trans_time.tv_nsec) {
+ be32toh(ntpmsg.origin_time.frac) != (unsigned long) m->trans_time.tv_nsec) {
log_debug("Invalid reply; not our transmit time. Ignoring.");
return 0;
}
diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
index 962285f7b1..bea800171b 100644
--- a/src/timesync/timesyncd.c
+++ b/src/timesync/timesyncd.c
@@ -66,6 +66,7 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) {
if (r < 0)
return log_error_errno(errno, "Failed to change file access mode: %m");
r = fchown(fd, uid, gid);
+ if (r < 0)
return log_error_errno(errno, "Failed to change file owner: %m");
}
@@ -96,7 +97,7 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) {
int main(int argc, char *argv[]) {
_cleanup_(manager_freep) Manager *m = NULL;
const char *user = "systemd-timesync";
- uid_t uid;
+ uid_t uid, uid_current;
gid_t gid;
int r;
@@ -113,10 +114,15 @@ int main(int argc, char *argv[]) {
goto finish;
}
- r = get_user_creds(&user, &uid, &gid, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "Cannot resolve user name %s: %m", user);
- goto finish;
+ uid = uid_current = geteuid();
+ gid = getegid();
+
+ if (uid_current == 0) {
+ r = get_user_creds(&user, &uid, &gid, NULL, NULL);
+ if (r < 0) {
+ log_error_errno(r, "Cannot resolve user name %s: %m", user);
+ goto finish;
+ }
}
r = load_clock_timestamp(uid, gid);
@@ -125,7 +131,7 @@ int main(int argc, char *argv[]) {
/* Drop privileges, but only if we have been started as root. If we are not running as root we assume all
* privileges are already dropped. */
- if (geteuid() == 0) {
+ if (uid_current == 0) {
r = drop_privileges(uid, gid, (1ULL << CAP_SYS_TIME));
if (r < 0)
goto finish;
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index a7ce1a8049..38cbb739c0 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -199,12 +199,12 @@ static const Specifier specifier_table[] = {
static int specifier_machine_id_safe(char specifier, void *data, void *userdata, char **ret) {
int r;
- /* If /etc/machine_id is missing (e.g. in a chroot environment), returns
- * a recognizable error so that the caller can skip the rule
+ /* If /etc/machine_id is missing or empty (e.g. in a chroot environment)
+ * return a recognizable error so that the caller can skip the rule
* gracefully. */
r = specifier_machine_id(specifier, data, userdata, ret);
- if (r == -ENOENT)
+ if (IN_SET(r, -ENOENT, -ENOMEDIUM))
return -ENXIO;
return r;
@@ -375,35 +375,47 @@ static struct Item* find_glob(OrderedHashmap *h, const char *match) {
static void load_unix_sockets(void) {
_cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
+ int r;
if (unix_sockets)
return;
- /* We maintain a cache of the sockets we found in
- * /proc/net/unix to speed things up a little. */
+ /* We maintain a cache of the sockets we found in /proc/net/unix to speed things up a little. */
unix_sockets = set_new(&string_hash_ops);
if (!unix_sockets)
return;
f = fopen("/proc/net/unix", "re");
- if (!f)
- return;
+ if (!f) {
+ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed to open /proc/net/unix, ignoring: %m");
+ goto fail;
+ }
/* Skip header */
- if (!fgets(line, sizeof(line), f))
+ r = read_line(f, LONG_LINE_MAX, NULL);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to skip /proc/net/unix header line: %m");
+ goto fail;
+ }
+ if (r == 0) {
+ log_warning("Premature end of file reading /proc/net/unix.");
goto fail;
+ }
for (;;) {
+ _cleanup_free_ char *line = NULL;
char *p, *s;
- int k;
- if (!fgets(line, sizeof(line), f))
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to read /proc/net/unix line, ignoring: %m");
+ goto fail;
+ }
+ if (r == 0) /* EOF */
break;
- truncate_nl(line);
-
p = strchr(line, ':');
if (!p)
continue;
@@ -420,21 +432,24 @@ static void load_unix_sockets(void) {
continue;
s = strdup(p);
- if (!s)
+ if (!s) {
+ log_oom();
goto fail;
+ }
path_kill_slashes(s);
- k = set_consume(unix_sockets, s);
- if (k < 0 && k != -EEXIST)
+ r = set_consume(unix_sockets, s);
+ if (r < 0 && r != -EEXIST) {
+ log_warning_errno(r, "Failed to add AF_UNIX socket to set, ignoring: %m");
goto fail;
+ }
}
return;
fail:
- set_free_free(unix_sockets);
- unix_sockets = NULL;
+ unix_sockets = set_free_free(unix_sockets);
}
static bool unix_socket_alive(const char *fn) {
@@ -532,11 +547,8 @@ static int dir_cleanup(
continue;
/* FUSE, NFS mounts, SELinux might return EACCES */
- if (errno == EACCES)
- log_debug_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name);
- else
- log_error_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name);
- r = -errno;
+ r = log_full_errno(errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
+ "stat(%s/%s) failed: %m", p, dent->d_name);
continue;
}
@@ -640,10 +652,8 @@ static int dir_cleanup(
log_debug("Removing directory \"%s\".", sub_path);
if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0)
- if (!IN_SET(errno, ENOENT, ENOTEMPTY)) {
- log_error_errno(errno, "rmdir(%s): %m", sub_path);
- r = -errno;
- }
+ if (!IN_SET(errno, ENOENT, ENOTEMPTY))
+ r = log_error_errno(errno, "rmdir(%s): %m", sub_path);
} else {
/* Skip files for which the sticky bit is
@@ -743,13 +753,50 @@ finish:
return r;
}
+static bool dangerous_hardlinks(void) {
+ _cleanup_free_ char *value = NULL;
+ static int cached = -1;
+ int r;
+
+ /* Check whether the fs.protected_hardlinks sysctl is on. If we can't determine it we assume its off, as that's
+ * what the upstream default is. */
+
+ if (cached >= 0)
+ return cached;
+
+ r = read_one_line_file("/proc/sys/fs/protected_hardlinks", &value);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read fs.protected_hardlinks sysctl: %m");
+ return true;
+ }
+
+ r = parse_boolean(value);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse fs.protected_hardlinks sysctl: %m");
+ return true;
+ }
+
+ cached = r == 0;
+ return cached;
+}
+
+static bool hardlink_vulnerable(struct stat *st) {
+ assert(st);
+
+ return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks();
+}
+
static int path_set_perms(Item *i, const char *path) {
+ char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_close_ int fd = -1;
struct stat st;
assert(i);
assert(path);
+ if (!i->mode_set && !i->uid_set && !i->gid_set)
+ goto shortcut;
+
/* We open the file with O_PATH here, to make the operation
* somewhat atomic. Also there's unfortunately no fchmodat()
* with AT_SYMLINK_NOFOLLOW, hence we emulate it here via
@@ -767,21 +814,23 @@ static int path_set_perms(Item *i, const char *path) {
}
log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path);
-
return r;
}
if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
- if (S_ISLNK(st.st_mode))
- log_debug("Skipping mode and owner fix for symlink %s.", path);
- else {
- char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
- xsprintf(fn, "/proc/self/fd/%i", fd);
+ if (hardlink_vulnerable(&st)) {
+ log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path);
+ return -EPERM;
+ }
- /* not using i->path directly because it may be a glob */
- if (i->mode_set) {
+ xsprintf(fn, "/proc/self/fd/%i", fd);
+
+ if (i->mode_set) {
+ if (S_ISLNK(st.st_mode))
+ log_debug("Skipping mode fix for symlink %s.", path);
+ else {
mode_t m = i->mode;
if (i->mask_perms) {
@@ -796,29 +845,32 @@ static int path_set_perms(Item *i, const char *path) {
}
if (m == (st.st_mode & 07777))
- log_debug("\"%s\" has right mode %o", path, st.st_mode);
+ log_debug("\"%s\" has correct mode %o already.", path, st.st_mode);
else {
- log_debug("chmod \"%s\" to mode %o", path, m);
+ log_debug("Changing \"%s\" to mode %o.", path, m);
+
if (chmod(fn, m) < 0)
return log_error_errno(errno, "chmod() of %s via %s failed: %m", path, fn);
}
}
+ }
- if ((i->uid != st.st_uid || i->gid != st.st_gid) &&
- (i->uid_set || i->gid_set)) {
- log_debug("chown \"%s\" to "UID_FMT"."GID_FMT,
- path,
- i->uid_set ? i->uid : UID_INVALID,
- i->gid_set ? i->gid : GID_INVALID);
- if (chown(fn,
- i->uid_set ? i->uid : UID_INVALID,
- i->gid_set ? i->gid : GID_INVALID) < 0)
- return log_error_errno(errno, "chown() of %s via %s failed: %m", path, fn);
- }
+ if ((i->uid_set && i->uid != st.st_uid) ||
+ (i->gid_set && i->gid != st.st_gid)) {
+ log_debug("Changing \"%s\" to owner "UID_FMT":"GID_FMT,
+ path,
+ i->uid_set ? i->uid : UID_INVALID,
+ i->gid_set ? i->gid : GID_INVALID);
+
+ if (chown(fn,
+ i->uid_set ? i->uid : UID_INVALID,
+ i->gid_set ? i->gid : GID_INVALID) < 0)
+ return log_error_errno(errno, "chown() of %s via %s failed: %m", path, fn);
}
fd = safe_close(fd);
+shortcut:
return label_fix(path, false, false);
}
@@ -867,11 +919,8 @@ static int path_set_xattrs(Item *i, const char *path) {
assert(path);
STRV_FOREACH_PAIR(name, value, i->xattrs) {
- int n;
-
- n = strlen(*value);
log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
- if (lsetxattr(path, *name, *value, n, 0) < 0)
+ if (lsetxattr(path, *name, *value, strlen(*value), 0) < 0)
return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m",
*name, *value, path);
}
@@ -960,6 +1009,11 @@ static int path_set_acls(Item *item, const char *path) {
if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
+ if (hardlink_vulnerable(&st)) {
+ log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path);
+ return -EPERM;
+ }
+
if (S_ISLNK(st.st_mode)) {
log_debug("Skipping ACL fix for symlink %s.", path);
return 0;
@@ -1135,7 +1189,7 @@ static int write_one_file(Item *i, const char *path) {
assert(i);
assert(path);
- flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW :
+ flags = i->type == CREATE_FILE ? O_CREAT|O_EXCL|O_NOFOLLOW :
i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0;
RUN_WITH_UMASK(0000) {
@@ -1146,9 +1200,13 @@ static int write_one_file(Item *i, const char *path) {
if (fd < 0) {
if (i->type == WRITE_FILE && errno == ENOENT) {
- log_debug_errno(errno, "Not writing \"%s\": %m", path);
+ log_debug_errno(errno, "Not writing missing file \"%s\": %m", path);
return 0;
}
+ if (i->type == CREATE_FILE && errno == EEXIST) {
+ log_debug_errno(errno, "Not writing to pre-existing file \"%s\": %m", path);
+ goto done;
+ }
r = -errno;
if (!i->argument && errno == EROFS && stat(path, &st) == 0 &&
@@ -1169,6 +1227,7 @@ static int write_one_file(Item *i, const char *path) {
fd = safe_close(fd);
+done:
if (stat(path, &st) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", path);
@@ -1289,14 +1348,24 @@ static int create_item(Item *i) {
case CREATE_FILE:
case TRUNCATE_FILE:
+ RUN_WITH_UMASK(0000)
+ (void) mkdir_parents_label(i->path, 0755);
+
r = write_one_file(i, i->path);
if (r < 0)
return r;
break;
case COPY_FILES: {
+
+ RUN_WITH_UMASK(0000)
+ (void) mkdir_parents_label(i->path, 0755);
+
log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path);
- r = copy_tree(i->argument, i->path, i->uid_set ? i->uid : UID_INVALID, i->gid_set ? i->gid : GID_INVALID, COPY_REFLINK);
+ r = copy_tree(i->argument, i->path,
+ i->uid_set ? i->uid : UID_INVALID,
+ i->gid_set ? i->gid : GID_INVALID,
+ COPY_REFLINK);
if (r == -EROFS && stat(i->path, &st) == 0)
r = -EEXIST;
@@ -1338,7 +1407,7 @@ static int create_item(Item *i) {
case CREATE_SUBVOLUME_INHERIT_QUOTA:
case CREATE_SUBVOLUME_NEW_QUOTA:
RUN_WITH_UMASK(0000)
- mkdir_parents_label(i->path, 0755);
+ (void) mkdir_parents_label(i->path, 0755);
if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) {
@@ -1420,6 +1489,8 @@ static int create_item(Item *i) {
case CREATE_FIFO:
RUN_WITH_UMASK(0000) {
+ (void) mkdir_parents_label(i->path, 0755);
+
mac_selinux_create_file_prepare(i->path, S_IFIFO);
r = mkfifo(i->path, i->mode);
mac_selinux_create_file_clear();
@@ -1462,6 +1533,9 @@ static int create_item(Item *i) {
}
case CREATE_SYMLINK: {
+ RUN_WITH_UMASK(0000)
+ (void) mkdir_parents_label(i->path, 0755);
+
mac_selinux_create_file_prepare(i->path, S_IFLNK);
r = symlink(i->argument, i->path);
mac_selinux_create_file_clear();
@@ -1520,6 +1594,9 @@ static int create_item(Item *i) {
return 0;
}
+ RUN_WITH_UMASK(0000)
+ (void) mkdir_parents_label(i->path, 0755);
+
file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR;
RUN_WITH_UMASK(0000) {
@@ -2254,6 +2331,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
}
} else {
existing = new0(ItemArray, 1);
+ if (!existing)
+ return log_oom();
+
r = ordered_hashmap_put(h, i.path, existing);
if (r < 0)
return log_oom();
@@ -2514,7 +2594,7 @@ int main(int argc, char *argv[]) {
}
}
- {
+ if (DEBUG_LOGGING) {
_cleanup_free_ char *t = NULL;
t = strv_join(config_dirs, "\n\t");
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 1553655a28..9dfb0d80de 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -159,7 +159,7 @@ static int ask_password_plymouth(
}
if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
- flush_fd(notify);
+ (void) flush_fd(notify);
if (pollfd[POLL_SOCKET].revents == 0)
continue;
@@ -417,8 +417,8 @@ static int wall_tty_block(void) {
if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
return log_oom();
- mkdir_parents_label(p, 0700);
- mkfifo(p, 0600);
+ (void) mkdir_parents_label(p, 0700);
+ (void) mkfifo(p, 0600);
fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0)
@@ -693,11 +693,13 @@ static int parse_argv(int argc, char *argv[]) {
* If one of the tasks does handle a password, the remaining tasks
* will be terminated.
*/
-static int ask_on_this_console(const char *tty, pid_t *pid, int argc, char *argv[]) {
+static int ask_on_this_console(const char *tty, pid_t *ret_pid, int argc, char *argv[]) {
struct sigaction sig = {
.sa_handler = nop_signal_handler,
.sa_flags = SA_NOCLDSTOP | SA_RESTART,
};
+ pid_t pid;
+ int r;
assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0);
@@ -707,18 +709,14 @@ static int ask_on_this_console(const char *tty, pid_t *pid, int argc, char *argv
sig.sa_handler = SIG_DFL;
assert_se(sigaction(SIGHUP, &sig, NULL) >= 0);
- *pid = fork();
- if (*pid < 0)
- return log_error_errno(errno, "Failed to fork process: %m");
-
- if (*pid == 0) {
+ r = safe_fork("(sd-passwd)", FORK_RESET_SIGNALS|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
int ac;
assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0);
- reset_signal_mask();
- reset_all_signal_handlers();
-
for (ac = 0; ac < argc; ac++) {
if (streq(argv[ac], "--console")) {
argv[ac] = strjoina("--console=", tty);
@@ -731,6 +729,8 @@ static int ask_on_this_console(const char *tty, pid_t *pid, int argc, char *argv
execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, argv);
_exit(EXIT_FAILURE);
}
+
+ *ret_pid = pid;
return 0;
}
diff --git a/src/udev/generate-keyboard-keys-gperf.sh b/src/udev/generate-keyboard-keys-gperf.sh
index eb977447e3..efb0da2a84 100755
--- a/src/udev/generate-keyboard-keys-gperf.sh
+++ b/src/udev/generate-keyboard-keys-gperf.sh
@@ -1,4 +1,5 @@
-#!/bin/sh -eu
+#!/bin/sh
+set -eu
awk ' BEGIN {
print "%{\n\
#if __GNUC__ >= 7\n\
diff --git a/src/udev/generate-keyboard-keys-list.sh b/src/udev/generate-keyboard-keys-list.sh
index 7a74e0dae1..c055f7c756 100755
--- a/src/udev/generate-keyboard-keys-list.sh
+++ b/src/udev/generate-keyboard-keys-list.sh
@@ -1,4 +1,5 @@
-#!/bin/sh -eu
+#!/bin/sh
+set -eu
$1 -dM -include linux/input.h - </dev/null | awk '
/\<(KEY_(MAX|MIN_INTERESTING))|(BTN_(MISC|MOUSE|JOYSTICK|GAMEPAD|DIGI|WHEEL|TRIGGER_HAPPY))\>/ { next }
diff --git a/src/udev/meson.build b/src/udev/meson.build
index d01cf8f194..de2fd2d9c4 100644
--- a/src/udev/meson.build
+++ b/src/udev/meson.build
@@ -113,15 +113,36 @@ if get_option('link-udev-shared')
udev_rpath = rootlibexecdir
else
udev_link_with = [libshared_static,
- libsystemd_internal]
+ libsystemd_static]
udev_rpath = ''
endif
-libudev_internal = static_library(
- 'udev',
+libudev_basic = static_library(
+ 'udev-basic',
libudev_sources,
include_directories : includes,
- link_with : udev_link_with)
+ c_args : ['-fvisibility=default'])
+
+libudev_static = static_library(
+ 'udev',
+ 'udev.h',
+ include_directories : includes,
+ link_with : udev_link_with,
+ link_whole : libudev_basic)
+
+libudev = shared_library(
+ 'udev',
+ 'udev.h', # pick a header file at random to work around old meson bug
+ version : libudev_version,
+ include_directories : includes,
+ link_args : ['-shared',
+ '-Wl,--version-script=' + libudev_sym_path],
+ link_with : [libsystemd_static, libshared_static],
+ link_whole : libudev_basic,
+ dependencies : [threads],
+ link_depends : libudev_sym,
+ install : true,
+ install_dir : rootlibdir)
libudev_core_includes = [includes, include_directories('net')]
libudev_core = static_library(
@@ -130,6 +151,7 @@ libudev_core = static_library(
link_config_gperf_c,
keyboard_keys_from_name_h,
include_directories : libudev_core_includes,
+ c_args : ['-DLOG_REALM=LOG_REALM_UDEV'],
link_with : udev_link_with,
dependencies : [libblkid, libkmod])
@@ -149,7 +171,7 @@ foreach prog : [['ata_id/ata_id.c'],
prog,
include_directories : includes,
c_args : ['-DLOG_REALM=LOG_REALM_UDEV'],
- link_with : [libudev_internal],
+ link_with : [libudev_static],
install_rpath : udev_rpath,
install : true,
install_dir : udevlibexecdir)
diff --git a/src/udev/mtd_probe/probe_smartmedia.c b/src/udev/mtd_probe/probe_smartmedia.c
index eb74fe1eb6..5d58de6a87 100644
--- a/src/udev/mtd_probe/probe_smartmedia.c
+++ b/src/udev/mtd_probe/probe_smartmedia.c
@@ -28,6 +28,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include "alloc-util.h"
#include "mtd_probe.h"
static const uint8_t cis_signature[] = {
@@ -35,16 +36,16 @@ static const uint8_t cis_signature[] = {
};
-void probe_smart_media(int mtd_fd, mtd_info_t* info)
-{
+void probe_smart_media(int mtd_fd, mtd_info_t* info) {
int sector_size;
int block_size;
int size_in_megs;
int spare_count;
- char* cis_buffer = malloc(SM_SECTOR_SIZE);
+ _cleanup_free_ uint8_t *cis_buffer = NULL;
int offset;
int cis_found = 0;
+ cis_buffer = malloc(SM_SECTOR_SIZE);
if (!cis_buffer)
return;
@@ -89,9 +90,8 @@ void probe_smart_media(int mtd_fd, mtd_info_t* info)
goto exit;
printf("MTD_FTL=smartmedia\n");
- free(cis_buffer);
- exit(0);
+ exit(EXIT_SUCCESS);
+
exit:
- free(cis_buffer);
return;
}
diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c
index 3ed8a51fd4..9bdaef8d90 100644
--- a/src/udev/net/ethtool-util.c
+++ b/src/udev/net/ethtool-util.c
@@ -25,13 +25,13 @@
#include "conf-parser.h"
#include "ethtool-util.h"
-#include "log.h"
#include "link-config.h"
+#include "log.h"
+#include "missing.h"
#include "socket-util.h"
#include "string-table.h"
#include "strxcpyx.h"
#include "util.h"
-#include "missing.h"
static const char* const duplex_table[_DUP_MAX] = {
[DUP_FULL] = "full",
@@ -83,6 +83,7 @@ int ethtool_connect(int *ret) {
fd = socket_ioctl_fd();
if (fd < 0)
return fd;
+
*ret = fd;
return 0;
@@ -265,7 +266,7 @@ int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) {
return 0;
}
-static int ethtool_get_stringset(int *fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) {
+static int get_stringset(int fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) {
_cleanup_free_ struct ethtool_gstrings *strings = NULL;
struct {
struct ethtool_sset_info info;
@@ -281,7 +282,7 @@ static int ethtool_get_stringset(int *fd, struct ifreq *ifr, int stringset_id, s
ifr->ifr_data = (void *) &buffer.info;
- r = ioctl(*fd, SIOCETHTOOL, ifr);
+ r = ioctl(fd, SIOCETHTOOL, ifr);
if (r < 0)
return -errno;
@@ -300,7 +301,7 @@ static int ethtool_get_stringset(int *fd, struct ifreq *ifr, int stringset_id, s
ifr->ifr_data = (void *) strings;
- r = ioctl(*fd, SIOCETHTOOL, ifr);
+ r = ioctl(fd, SIOCETHTOOL, ifr);
if (r < 0)
return -errno;
@@ -335,7 +336,7 @@ int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) {
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
- r = ethtool_get_stringset(fd, &ifr, ETH_SS_FEATURES, &strings);
+ r = get_stringset(*fd, &ifr, ETH_SS_FEATURES, &strings);
if (r < 0)
return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname);
@@ -374,7 +375,7 @@ int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) {
return 0;
}
-static int get_glinksettings(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **g) {
+static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **g) {
struct ecmd {
struct ethtool_link_settings req;
__u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
@@ -395,29 +396,29 @@ static int get_glinksettings(int *fd, struct ifreq *ifr, struct ethtool_link_use
ifr->ifr_data = (void *) &ecmd;
- r = ioctl(*fd, SIOCETHTOOL, ifr);
+ r = ioctl(fd, SIOCETHTOOL, ifr);
if (r < 0)
return -errno;
if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
ifr->ifr_data = (void *) &ecmd;
- r = ioctl(*fd, SIOCETHTOOL, ifr);
+ r = ioctl(fd, SIOCETHTOOL, ifr);
if (r < 0)
return -errno;
if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
u = new0(struct ethtool_link_usettings , 1);
if (!u)
return -ENOMEM;
- memcpy(&u->base, &ecmd.req, sizeof(struct ethtool_link_settings));
+ ecmd.req = u->base;
offset = 0;
memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
@@ -433,7 +434,7 @@ static int get_glinksettings(int *fd, struct ifreq *ifr, struct ethtool_link_use
return 0;
}
-static int get_gset(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **u) {
+static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **u) {
struct ethtool_link_usettings *e;
struct ethtool_cmd ecmd = {
.cmd = ETHTOOL_GSET,
@@ -442,7 +443,7 @@ static int get_gset(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **
ifr->ifr_data = (void *) &ecmd;
- r = ioctl(*fd, SIOCETHTOOL, ifr);
+ r = ioctl(fd, SIOCETHTOOL, ifr);
if (r < 0)
return -errno;
@@ -469,19 +470,19 @@ static int get_gset(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **
return 0;
}
-static int set_slinksettings(int *fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
+static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
struct {
struct ethtool_link_settings req;
__u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
- } ecmd = {
- .req.cmd = ETHTOOL_SLINKSETTINGS,
- };
+ } ecmd = {};
unsigned int offset;
int r;
if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
return -EINVAL;
+ ecmd.req = u->base;
+ ecmd.req.cmd = ETHTOOL_SLINKSETTINGS;
offset = 0;
memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords);
@@ -493,14 +494,14 @@ static int set_slinksettings(int *fd, struct ifreq *ifr, const struct ethtool_li
ifr->ifr_data = (void *) &ecmd;
- r = ioctl(*fd, SIOCETHTOOL, ifr);
+ r = ioctl(fd, SIOCETHTOOL, ifr);
if (r < 0)
return -errno;
return 0;
}
-static int set_sset(int *fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
+static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
struct ethtool_cmd ecmd = {
.cmd = ETHTOOL_SSET,
};
@@ -523,7 +524,7 @@ static int set_sset(int *fd, struct ifreq *ifr, const struct ethtool_link_usetti
ifr->ifr_data = (void *) &ecmd;
- r = ioctl(*fd, SIOCETHTOOL, ifr);
+ r = ioctl(fd, SIOCETHTOOL, ifr);
if (r < 0)
return -errno;
@@ -555,10 +556,9 @@ int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *l
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
- r = get_glinksettings(fd, &ifr, &u);
+ r = get_glinksettings(*fd, &ifr, &u);
if (r < 0) {
-
- r = get_gset(fd, &ifr, &u);
+ r = get_gset(*fd, &ifr, &u);
if (r < 0)
return log_warning_errno(r, "link_config: Cannot get device settings for %s : %m", ifname);
}
@@ -570,15 +570,14 @@ int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *l
u->base.duplex = link->duplex;
if (link->port != _NET_DEV_PORT_INVALID)
- u->base.port = link->port;
+ u->base.port = link->port;
u->base.autoneg = link->autonegotiation;
if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
- r = set_slinksettings(fd, &ifr, u);
+ r = set_slinksettings(*fd, &ifr, u);
else
- r = set_sset(fd, &ifr, u);
-
+ r = set_sset(*fd, &ifr, u);
if (r < 0)
return log_warning_errno(r, "link_config: Cannot set device settings for %s : %m", ifname);
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
index 85f0a0625b..5cb126d870 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -26,7 +26,8 @@ Match.Driver, config_parse_strv, 0,
Match.Type, config_parse_strv, 0, offsetof(link_config, match_type)
Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, match_host)
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel_cmdline)
+Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(link_config, match_kernel_version)
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, match_arch)
Link.Description, config_parse_string, 0, offsetof(link_config, description)
Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy)
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index 89891f9e27..a4368f088d 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -77,7 +77,8 @@ static void link_config_free(link_config *link) {
free(link->match_name);
free(link->match_host);
free(link->match_virt);
- free(link->match_kernel);
+ free(link->match_kernel_cmdline);
+ free(link->match_kernel_version);
free(link->match_arch);
free(link->description);
@@ -171,7 +172,7 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
link->port = _NET_DEV_PORT_INVALID;
link->autonegotiation = -1;
- memset(&link->features, -1, sizeof(link->features));
+ memset(&link->features, 0xFF, sizeof(link->features));
r = config_parse(NULL, filename, file,
"Match\0Link\0Ethernet\0",
@@ -186,6 +187,8 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
return -ERANGE;
link->filename = strdup(filename);
+ if (!link->filename)
+ return log_oom();
LIST_PREPEND(links, ctx->links, link);
link = NULL;
@@ -246,7 +249,8 @@ int link_config_get(link_config_ctx *ctx, struct udev_device *device,
if (net_match_config(link->match_mac, link->match_path, link->match_driver,
link->match_type, link->match_name, link->match_host,
- link->match_virt, link->match_kernel, link->match_arch,
+ link->match_virt, link->match_kernel_cmdline,
+ link->match_kernel_version, link->match_arch,
attr_value ? ether_aton(attr_value) : NULL,
udev_device_get_property_value(device, "ID_PATH"),
udev_device_get_driver(udev_device_get_parent(device)),
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index a413251bd0..dabc3eff8f 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -58,7 +58,8 @@ struct link_config {
char **match_name;
Condition *match_host;
Condition *match_virt;
- Condition *match_kernel;
+ Condition *match_kernel_cmdline;
+ Condition *match_kernel_version;
Condition *match_arch;
char *description;
diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c
index ab4ee7b00b..32c1a8def4 100644
--- a/src/udev/scsi_id/scsi_id.c
+++ b/src/udev/scsi_id/scsi_id.c
@@ -358,7 +358,7 @@ static int set_options(struct udev *udev,
case 'h':
help();
- exit(0);
+ exit(EXIT_SUCCESS);
case 'p':
if (streq(optarg, "0x80"))
@@ -393,7 +393,7 @@ static int set_options(struct udev *udev,
case 'V':
printf("%s\n", PACKAGE_VERSION);
- exit(0);
+ exit(EXIT_SUCCESS);
case 'x':
export = true;
@@ -608,7 +608,7 @@ int main(int argc, char **argv)
* Get command line options (overriding any config file settings).
*/
if (set_options(udev, argc, argv, maj_min_dev) < 0)
- exit(1);
+ exit(EXIT_FAILURE);
if (!dev_specified) {
log_error("No device specified.");
diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c
index b13fdb135b..fa830213ff 100644
--- a/src/udev/udev-builtin-input_id.c
+++ b/src/udev/udev-builtin-input_id.c
@@ -102,8 +102,7 @@ static void get_cap_mask(struct udev_device *dev,
unsigned long val;
v = udev_device_get_sysattr_value(pdev, attr);
- if (!v)
- v = "";
+ v = strempty(v);
xsprintf(text, "%s", v);
log_debug("%s raw kernel attribute: %s", attr, text);
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index f3be27fdd0..945585d82a 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -547,7 +547,7 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) {
* verify each bus-ID part...
*/
bus_id_len = strlen(bus_id);
- if (!bus_id_len || bus_id_len < 8 || bus_id_len > 9)
+ if (!IN_SET(bus_id_len, 8, 9))
return -EINVAL;
/* Strip leading zeros from the bus id for aesthetic purposes. This
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index 8f7c28f03d..d0befba29c 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -774,10 +774,10 @@ int udev_event_spawn(struct udev_event *event,
}
}
- pid = fork();
- switch(pid) {
- case 0:
- {
+ err = safe_fork("(spawn)", FORK_RESET_SIGNALS|FORK_LOG, &pid);
+ if (err < 0)
+ goto out;
+ if (err == 0) {
char arg[UTIL_PATH_SIZE];
char *argv[128];
char program[UTIL_PATH_SIZE];
@@ -802,23 +802,18 @@ int udev_event_spawn(struct udev_event *event,
_exit(2);
}
- case -1:
- log_error_errno(errno, "fork of '%s' failed: %m", cmd);
- err = -1;
- goto out;
- default:
- /* parent closed child's ends of pipes */
- outpipe[WRITE_END] = safe_close(outpipe[WRITE_END]);
- errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]);
- spawn_read(event,
- timeout_usec,
- cmd,
- outpipe[READ_END], errpipe[READ_END],
- result, ressize);
+ /* parent closed child's ends of pipes */
+ outpipe[WRITE_END] = safe_close(outpipe[WRITE_END]);
+ errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]);
- err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure);
- }
+ spawn_read(event,
+ timeout_usec,
+ cmd,
+ outpipe[READ_END], errpipe[READ_END],
+ result, ressize);
+
+ err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure);
out:
if (outpipe[READ_END] >= 0)
diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c
index d80d61583d..9546a6ebaf 100644
--- a/src/udev/udevadm-control.c
+++ b/src/udev/udevadm-control.c
@@ -21,6 +21,7 @@
#include <string.h>
#include <unistd.h>
+#include "process-util.h"
#include "time-util.h"
#include "udev-util.h"
#include "udev.h"
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 1644935ff9..5c757d513f 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1700,9 +1700,9 @@ int main(int argc, char *argv[]) {
goto exit;
}
- r = mkdir("/run/udev", 0755);
- if (r < 0 && errno != EEXIST) {
- r = log_error_errno(errno, "could not create /run/udev: %m");
+ r = mkdir_errno_wrapper("/run/udev", 0755);
+ if (r < 0 && r != -EEXIST) {
+ log_error_errno(r, "could not create /run/udev: %m");
goto exit;
}
diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c
index e19a1637bf..2e0e09d843 100644
--- a/src/vconsole/vconsole-setup.c
+++ b/src/vconsole/vconsole-setup.c
@@ -133,6 +133,7 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m
const char *args[8];
unsigned i = 0;
pid_t pid;
+ int r;
/* An empty map means kernel map */
if (isempty(map))
@@ -152,19 +153,15 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m
log_debug("Executing \"%s\"...",
strnull((cmd = strv_join((char**) args, " "))));
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
- else if (pid == 0) {
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
+ r = safe_fork("(loadkeys)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
execv(args[0], (char **) args);
_exit(EXIT_FAILURE);
}
- return wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true);
+ return wait_for_terminate_and_check(KBD_LOADKEYS, pid, WAIT_LOG);
}
static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
@@ -172,6 +169,7 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map,
const char *args[9];
unsigned i = 0;
pid_t pid;
+ int r;
/* Any part can be set independently */
if (isempty(font) && isempty(map) && isempty(unimap))
@@ -195,19 +193,15 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map,
log_debug("Executing \"%s\"...",
strnull((cmd = strv_join((char**) args, " "))));
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
- else if (pid == 0) {
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
+ r = safe_fork("(setfont)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
execv(args[0], (char **) args);
_exit(EXIT_FAILURE);
}
- return wait_for_terminate_and_warn(KBD_SETFONT, pid, true);
+ return wait_for_terminate_and_check(KBD_SETFONT, pid, WAIT_LOG);
}
/*
@@ -458,8 +452,8 @@ int main(int argc, char **argv) {
log_warning_errno(r, "Failed to read /proc/cmdline: %m");
}
- toggle_utf8_sysfs(utf8);
- toggle_utf8(vc, fd, utf8);
+ (void) toggle_utf8_sysfs(utf8);
+ (void) toggle_utf8(vc, fd, utf8);
r = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap);
keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) == 0;
diff --git a/src/veritysetup/veritysetup-generator.c b/src/veritysetup/veritysetup-generator.c
index 5919b1380e..d09f295392 100644
--- a/src/veritysetup/veritysetup-generator.c
+++ b/src/veritysetup/veritysetup-generator.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/stat.h>
@@ -27,6 +28,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "fstab-util.h"
+#include "generator.h"
#include "hexdecoct.h"
#include "id128-util.h"
#include "mkdir.h"
@@ -36,6 +38,8 @@
#include "string-util.h"
#include "unit-name.h"
+#define SYSTEMD_VERITYSETUP_SERVICE "systemd-veritysetup@root.service"
+
static char *arg_dest = NULL;
static bool arg_enabled = true;
static char *arg_root_hash = NULL;
@@ -45,7 +49,7 @@ static char *arg_hash_what = NULL;
static int create_device(void) {
_cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL, *u_escaped = NULL, *v_escaped = NULL, *root_hash_escaped = NULL;
_cleanup_fclose_ FILE *f = NULL;
- const char *p, *to;
+ const char *to;
int r;
/* If all three pieces of information are missing, then verity is turned off */
@@ -67,8 +71,6 @@ static int create_device(void) {
" hash device %s,\n"
" and root hash %s.", arg_data_what, arg_hash_what, arg_root_hash);
- p = strjoina(arg_dest, "/systemd-veritysetup@root.service");
-
u = fstab_node_to_udev_node(arg_data_what);
if (!u)
return log_oom();
@@ -94,12 +96,11 @@ static int create_device(void) {
if (!root_hash_escaped)
return log_oom();
- f = fopen(p, "wxe");
- if (!f)
- return log_error_errno(errno, "Failed to create unit file %s: %m", p);
+ r = generator_open_unit_file(arg_dest, NULL, SYSTEMD_VERITYSETUP_SERVICE, &f);
+ if (r < 0)
+ return r;
fprintf(f,
- "# Automatically generated by systemd-veritysetup-generator\n\n"
"[Unit]\n"
"Description=Integrity Protection Setup for %%I\n"
"Documentation=man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n"
@@ -121,12 +122,12 @@ static int create_device(void) {
r = fflush_and_check(f);
if (r < 0)
- return log_error_errno(r, "Failed to write file %s: %m", p);
+ return log_error_errno(r, "Failed to write file unit "SYSTEMD_VERITYSETUP_SERVICE": %m");
- to = strjoina(arg_dest, "/cryptsetup.target.requires/systemd-veritysetup@root.service");
+ to = strjoina(arg_dest, "/cryptsetup.target.requires/" SYSTEMD_VERITYSETUP_SERVICE);
(void) mkdir_parents(to, 0755);
- if (symlink("../systemd-veritysetup@root.service", to) < 0)
+ if (symlink("../" SYSTEMD_VERITYSETUP_SERVICE, to) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", to);
return 0;
@@ -227,7 +228,8 @@ int main(int argc, char *argv[]) {
if (argc > 1)
arg_dest = argv[1];
- log_set_target(LOG_TARGET_SAFE);
+ log_set_prohibit_ipc(true);
+ log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c
index d3066ca429..3b4e72bf9e 100644
--- a/src/veritysetup/veritysetup.c
+++ b/src/veritysetup/veritysetup.c
@@ -18,14 +18,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
+#include "alloc-util.h"
#include "crypt-util.h"
-#include "log.h"
#include "hexdecoct.h"
+#include "log.h"
#include "string-util.h"
-#include "alloc-util.h"
static char *arg_root_hash = NULL;
static char *arg_data_what = NULL;